1# Soft Serve23<p>4 <img style="width: 451px" src="https://stuff.charm.sh/soft-serve/soft-serve-header.png?0" alt="A nice rendering of some melting ice cream with the words ‘Charm Soft Serve’ next to it"><br>5 <a href="https://github.com/charmbracelet/soft-serve/releases"><img src="https://img.shields.io/github/release/charmbracelet/soft-serve.svg" alt="Latest Release"></a>6 <a href="https://pkg.go.dev/github.com/charmbracelet/soft-serve?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>7 <a href="https://github.com/charmbracelet/soft-serve/actions"><img src="https://github.com/charmbracelet/soft-serve/workflows/build/badge.svg" alt="Build Status"></a>8 <a href="https://nightly.link/charmbracelet/soft-serve/workflows/nightly/main"><img src="https://shields.io/badge/-Nightly%20Builds-orange?logo=hackthebox&logoColor=fff&style=appveyor"/></a>9</p>1011A tasty, self-hostable Git server for the command line. 🍦1213<picture>14 <source media="(max-width: 750px)" srcset="https://github.com/charmbracelet/soft-serve/assets/42545625/c754c746-dc4c-44a6-9c39-28649264cbf2">15 <source media="(min-width: 750px)" width="750" srcset="https://github.com/charmbracelet/soft-serve/assets/42545625/c754c746-dc4c-44a6-9c39-28649264cbf2">16 <img src="https://github.com/charmbracelet/soft-serve/assets/42545625/c754c746-dc4c-44a6-9c39-28649264cbf2" alt="Soft Serve screencast">17</picture>1819- Easy to navigate TUI available over SSH20- Clone repos over SSH, HTTP, or Git protocol21- Git LFS support with both HTTP and SSH backends22- Manage repos with SSH23- Create repos on demand with SSH or `git push`24- Browse repos, files and commits with SSH-accessible UI25- Print files over SSH with or without syntax highlighting and line numbers26- Easy access control27 - SSH authentication using public keys28 - Allow/disallow anonymous access29 - Add collaborators with SSH public keys30 - Repos can be public or private31 - User access tokens3233## Where can I see it?3435Just run `ssh git.charm.sh` for an example. You can also try some of the following commands:3637```bash38# Jump directly to a repo in the TUI39ssh git.charm.sh -t soft-serve4041# Print out a directory tree for a repo42ssh git.charm.sh repo tree soft-serve4344# Print a specific file45ssh git.charm.sh repo blob soft-serve cmd/soft/main.go4647# Print a file with syntax highlighting and line numbers48ssh git.charm.sh repo blob soft-serve cmd/soft/main.go -c -l49```5051Or you can use Soft Serve to browse local repositories using `soft browse52[directory]` or running `soft` within a Git repository.5354## Installation5556Soft Serve is a single binary called `soft`. You can get it from a package57manager:5859```bash60# macOS or Linux61brew install charmbracelet/tap/soft-serve6263# Windows (with Winget)64winget install charmbracelet.soft-serve6566# Arch Linux67pacman -S soft-serve6869# Nix70nix-env -iA nixpkgs.soft-serve7172# Debian/Ubuntu73sudo mkdir -p /etc/apt/keyrings74curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg75echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list76sudo apt update && sudo apt install soft-serve7778# Fedora/RHEL79echo '[charm]80name=Charm81baseurl=https://repo.charm.sh/yum/82enabled=183gpgcheck=184gpgkey=https://repo.charm.sh/yum/gpg.key' | sudo tee /etc/yum.repos.d/charm.repo85sudo yum install soft-serve86```8788You can also download a binary from the [releases][releases] page. Packages are89available in Alpine, Debian, and RPM formats. Binaries are available for Linux,90macOS, and Windows.9192[releases]: https://github.com/charmbracelet/soft-serve/releases9394Or just install it with `go`:9596```bash97go install github.com/charmbracelet/soft-serve/cmd/soft@latest98```99100A [Docker image][docker] is also available.101102[docker]: https://github.com/charmbracelet/soft-serve/blob/main/docker.md103104## Setting up a server105106Make sure `git` is installed, then run `soft serve`. That’s it.107108This will create a `data` directory that will store all the repos, ssh keys,109and database.110111By default, program configuration is stored within the `data` directory. But,112this can be overridden by setting a custom path to a config file with `SOFT_SERVE_CONFIG_LOCATION`113that is pre-created. If a config file pointed to by `SOFT_SERVE_CONFIG_LOCATION`,114the default location within the `data` dir is used for generating a default config.115116To change the default data path use `SOFT_SERVE_DATA_PATH` environment variable.117118```sh119SOFT_SERVE_DATA_PATH=/var/lib/soft-serve soft serve120```121122When you run Soft Serve for the first time, make sure you have the123`SOFT_SERVE_INITIAL_ADMIN_KEYS` environment variable is set to your ssh124authorized key. Any added key to this variable will be treated as admin with125full privileges.126127Using this environment variable, Soft Serve will create a new `admin` user that128has full privileges. You can rename and change the user settings later.129130Check out [Systemd][systemd] on how to run Soft Serve as a service using131Systemd. Soft Serve packages in our Apt/Yum repositories come with Systemd132service units.133134[systemd]: https://github.com/charmbracelet/soft-serve/blob/main/systemd.md135136### Server Configuration137138Once you start the server for the first time, the settings will be in139`config.yaml` under your data directory. The default `config.yaml` is140self-explanatory and will look like this:141142```yaml143# Soft Serve Server configurations144145# The name of the server.146# This is the name that will be displayed in the UI.147name: "Soft Serve"148149# Log format to use. Valid values are "json", "logfmt", and "text".150log_format: "text"151152# The SSH server configuration.153ssh:154 # The address on which the SSH server will listen.155 listen_addr: ":23231"156157 # The public URL of the SSH server.158 # This is the address that will be used to clone repositories.159 public_url: "ssh://localhost:23231"160161 # The path to the SSH server's private key.162 key_path: "ssh/soft_serve_host"163164 # The path to the SSH server's client private key.165 # This key will be used to authenticate the server to make git requests to166 # ssh remotes.167 client_key_path: "ssh/soft_serve_client"168169 # The maximum number of seconds a connection can take.170 # A value of 0 means no timeout.171 max_timeout: 0172173 # The number of seconds a connection can be idle before it is closed.174 idle_timeout: 120175176# The Git daemon configuration.177git:178 # The address on which the Git daemon will listen.179 listen_addr: ":9418"180181 # The maximum number of seconds a connection can take.182 # A value of 0 means no timeout.183 max_timeout: 0184185 # The number of seconds a connection can be idle before it is closed.186 idle_timeout: 3187188 # The maximum number of concurrent connections.189 max_connections: 32190191# The HTTP server configuration.192http:193 # The address on which the HTTP server will listen.194 listen_addr: ":23232"195196 # The path to the TLS private key.197 tls_key_path: ""198199 # The path to the TLS certificate.200 tls_cert_path: ""201202 # The public URL of the HTTP server.203 # This is the address that will be used to clone repositories.204 # Make sure to use https:// if you are using TLS.205 public_url: "http://localhost:23232"206207 # The cross-origin request security options208 cors:209 # The allowed cross-origin headers210 allowed_headers:211 - "Accept"212 - "Accept-Language"213 - "Content-Language"214 - "Content-Type"215 - "Origin"216 - "X-Requested-With"217 - "User-Agent"218 - "Authorization"219 - "Access-Control-Request-Method"220 - "Access-Control-Allow-Origin"221222 # The allowed cross-origin URLs223 allowed_origins:224 - "http://localhost:23232" # always allowed225 # - "https://example.com"226227 # The allowed cross-origin methods228 allowed_methods:229 - "GET"230 - "HEAD"231 - "POST"232 - "PUT"233 - "OPTIONS"234235# The database configuration.236db:237 # The database driver to use.238 # Valid values are "sqlite" and "postgres".239 driver: "sqlite"240 # The database data source name.241 # This is driver specific and can be a file path or connection string.242 # Make sure foreign key support is enabled when using SQLite.243 data_source: "soft-serve.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)"244245# Git LFS configuration.246lfs:247 # Enable Git LFS.248 enabled: true249 # Enable Git SSH transfer.250 ssh_enabled: false251252# Cron job configuration253jobs:254 mirror_pull: "@every 10m"255256# The stats server configuration.257stats:258 # The address on which the stats server will listen.259 listen_addr: ":23233"260# Additional admin keys.261#initial_admin_keys:262# - "ssh-rsa AAAAB3NzaC1yc2..."263```264265You can also use environment variables, to override these settings. All server266settings environment variables start with `SOFT_SERVE_` followed by the setting267name all in uppercase. Here are some examples:268269- `SOFT_SERVE_NAME`: The name of the server that will appear in the TUI270- `SOFT_SERVE_SSH_LISTEN_ADDR`: SSH listen address271- `SOFT_SERVE_SSH_KEY_PATH`: SSH host key-pair path272- `SOFT_SERVE_HTTP_LISTEN_ADDR`: HTTP listen address273- `SOFT_SERVE_HTTP_PUBLIC_URL`: HTTP public URL used for cloning274- `SOFT_SERVE_GIT_MAX_CONNECTIONS`: The number of simultaneous connections to git daemon275276#### Database Configuration277278Soft 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.279280To use Postgres as your database, first create a Soft Serve database:281282```sh283psql -h<hostname> -p<port> -U<user> -c 'CREATE DATABASE soft_serve'284```285286Then 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:287288```289db:290 driver: "postgres"291 data_source: "postgres://postgres@localhost:5432/soft_serve?sslmode=disable"292```293294Environment variables equivalent:295296```sh297SOFT_SERVE_DB_DRIVER=postgres \298SOFT_SERVE_DB_DATA_SOURCE="postgres://postgres@localhost:5432/soft_serve?sslmode=disable" \299soft serve300```301302You can specify a database connection password in the _data source_ url. For example, `postgres://myuser:dbpass@localhost:5432/my_soft_serve_db`.303304#### LFS Configuration305306Soft 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.307308Use the `lfs` config section to customize your Git LFS server.309310> **Note**: The pure-SSH transfer is disabled by default.311312## Server Access313314Soft 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.315316To manage the server users, access, and repos, you can use the SSH command line interface.317318Try `ssh localhost -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes -p 23231 help` for more info. Make sure319you use your key here.320321> **Note** The `IdentitiesOnly` option is used to prevent SSH from using any322> other keys in your `~/.ssh` directory. This is useful when you have multiple323> keys, and you want to use a specific key for Soft Serve.324325For ease of use, instead of specifying the key, port, and hostname every time326you SSH into Soft Serve, add your own Soft Serve instance entry to your SSH327config. For instance, to use `ssh soft` instead of typing `ssh localhost -i328~/.ssh/id_ed25519 -o IdentitiesOnly=yes -p 23231`, we can define a `soft` entry in our SSH config329file `~/.ssh/config`.330331```conf332Host soft333 HostName localhost334 Port 23231335 IdentityFile ~/.ssh/id_ed25519336 IdentitiesOnly yes337```338339Now, we can do `ssh soft` to SSH into Soft Serve. Since `git` is also aware of340this config, you can use `soft` as the hostname for your clone commands.341342```sh343git clone ssh://soft/dotfiles344# make changes345# add & commit346git push origin main347```348349> **Note** The `-i` and `-o` parts will be omitted in the examples below for brevity. You350> can add your server settings to your sshconfig for quicker access.351352### Authentication353354Everything that needs authentication is done using SSH. Make sure you have355added an entry for your Soft Serve instance in your `~/.ssh/config` file.356357By default, Soft Serve gives read-only permission to anonymous connections to358any of the above protocols. This is controlled by two settings `anon-access`359and `allow-keyless`.360361- `anon-access`: Defines the access level for anonymous users. Available362 options are `no-access`, `read-only`, `read-write`, and `admin-access`.363 Default is `read-only`.364- `allow-keyless`: Whether to allow connections that doesn't use keys to pass.365 Setting this to `false` would disable access to SSH keyboard-interactive,366 HTTP, and Git protocol connections. Default is `true`.367368```sh369$ ssh -p 23231 localhost settings370Manage server settings371372Usage:373 ssh -p 23231 localhost settings [command]374375Available Commands:376 allow-keyless Set or get allow keyless access to repositories377 anon-access Set or get the default access level for anonymous users378379Flags:380 -h, --help help for settings381382Use "ssh -p 23231 localhost settings [command] --help" for more information about a command.383```384385> **Note** These settings can only be changed by admins.386387When `allow-keyless` is disabled, connections that don't use SSH Public Key388authentication will get denied. This means cloning repos over HTTP(s) or git://389will get denied.390391Meanwhile, `anon-access` controls the access level granted to connections that392use SSH Public Key authentication but are not registered users. The default393setting for this is `read-only`. This will grant anonymous connections that use394SSH Public Key authentication `read-only` access to public repos.395396`anon-access` is also used in combination with `allow-keyless` to determine the397access level for HTTP(s) and git:// clone requests.398399#### SSH400401Soft Serve doesn't allow duplicate SSH public keys for users. A public key can be associated with one user only. This makes SSH authentication simple and straight forward, add your public key to your Soft Serve user to be able to access Soft Serve.402403#### HTTP404405You can generate user access tokens through the SSH command line interface. Access tokens can have an optional expiration date. Use your access token as the basic auth user to access your Soft Serve repos through HTTP.406407```sh408# Create a user token409ssh -p 23231 localhost token create 'my new token'410ss_1234abc56789012345678901234de246d798fghi411412# Or with an expiry date413ssh -p 23231 localhost token create --expires-in 1y 'my other token'414ss_98fghi1234abc56789012345678901234de246d7415```416417Now you can access to repos that require `read-write` access.418419```sh420git clone http://ss_98fghi1234abc56789012345678901234de246d7@localhost:23232/my-private-repo.git my-private-repo421# Make changes and push422```423424### Authorization425426Soft Serve offers a simple access control. There are four access levels,427no-access, read-only, read-write, and admin-access.428429`admin-access` has full control of the server and can make changes to users and repos.430431`read-write` access gets full control of repos.432433`read-only` can read public repos.434435`no-access` denies access to all repos.436437## User Management438439Admins can manage users and their keys using the `user` command. Once a user is440created and has access to the server, they can manage their own keys and441settings.442443To create a new user simply use `user create`:444445```sh446# Create a new user447ssh -p 23231 localhost user create beatrice448449# Add user keys450ssh -p 23231 localhost user add-pubkey beatrice ssh-rsa AAAAB3Nz...451ssh -p 23231 localhost user add-pubkey beatrice ssh-ed25519 AAAA...452453# Create another user with public key454ssh -p 23231 localhost user create frankie '-k "ssh-ed25519 AAAATzN..."'455456# Need help?457ssh -p 23231 localhost user help458```459460Once a user is created, they get `read-only` access to public repositories.461They can also create new repositories on the server.462463Users can manage their keys using the `pubkey` command:464465```sh466# List user keys467ssh -p 23231 localhost pubkey list468469# Add key470ssh -p 23231 localhost pubkey add ssh-ed25519 AAAA...471472# Wanna change your username?473ssh -p 23231 localhost set-username yolo474475# To display user info476ssh -p 23231 localhost info477```478479## Repositories480481You can manage repositories using the `repo` command.482483```sh484# Run repo help485$ ssh -p 23231 localhost repo help486Manage repositories487488Usage:489 ssh -p 23231 localhost repo [command]490491Aliases:492 repo, repos, repository, repositories493494Available Commands:495 blob Print out the contents of file at path496 branch Manage repository branches497 collab Manage collaborators498 create Create a new repository499 delete Delete a repository500 description Set or get the description for a repository501 hide Hide or unhide a repository502 import Import a new repository from remote503 info Get information about a repository504 is-mirror Whether a repository is a mirror505 list List repositories506 private Set or get a repository private property507 project-name Set or get the project name for a repository508 rename Rename an existing repository509 tag Manage repository tags510 tree Print repository tree at path511512Flags:513 -h, --help help for repo514515Use "ssh -p 23231 localhost repo [command] --help" for more information about a command.516```517518To use any of the above `repo` commands, a user must be a collaborator in the repository. More on this below.519520### Creating Repositories521522To create a repository, first make sure you are a registered user. Use the523`repo create <repo>` command to create a new repository:524525```sh526# Create a new repository527ssh -p 23231 localhost repo create icecream528529# Create a repo with description530ssh -p 23231 localhost repo create icecream '-d "This is an Ice Cream description"'531532# ... and project name533ssh -p 23231 localhost repo create icecream '-d "This is an Ice Cream description"' '-n "Ice Cream"'534535# I need my repository private!536ssh -p 23231 localhost repo create icecream -p '-d "This is an Ice Cream description"' '-n "Ice Cream"'537538# Help?539ssh -p 23231 localhost repo create -h540```541542Or you can add your Soft Serve server as a remote to any existing repo, given543you have write access, and push to remote:544545```546git remote add origin ssh://localhost:23231/icecream547```548549After you’ve added the remote just go ahead and push. If the repo doesn’t exist550on the server it’ll be created.551552```553git push origin main554```555556### Nested Repositories557558Repositories can be nested too:559560```sh561# Create a new nested repository562ssh -p 23231 localhost repo create charmbracelet/icecream563564# Or ...565git remote add charm ssh://localhost:23231/charmbracelet/icecream566git push charm main567```568569### Mirrors570571You can also *import* repositories from any public remote. Use the `repo import` command.572573```sh574ssh -p 23231 localhost repo import soft-serve https://github.com/charmbracelet/soft-serve575```576577Use `--mirror` or `-m` to mark the repository as a *pull* mirror.578579### Deleting Repositories580581You can delete repositories using the `repo delete <repo>` command.582583```sh584ssh -p 23231 localhost repo delete icecream585```586587### Renaming Repositories588589Use the `repo rename <old> <new>` command to rename existing repositories.590591```sh592ssh -p 23231 localhost repo rename icecream vanilla593```594595### Repository Collaborators596597Sometimes you want to restrict write access to certain repositories. This can598be achieved by adding a collaborator to your repository.599600Use the `repo collab <command> <repo>` command to manage repo collaborators.601602```sh603# Add collaborator to soft-serve604ssh -p 23231 localhost repo collab add soft-serve frankie605606# Add collaborator with a specific access level607ssh -p 23231 localhost repo collab add soft-serve beatrice read-only608609# Remove collaborator610ssh -p 23231 localhost repo collab remove soft-serve beatrice611612# List collaborators613ssh -p 23231 localhost repo collab list soft-serve614```615616### Repository Metadata617618You can also change the repo's description, project name, whether it's private,619etc using the `repo <command>` command.620621```sh622# Set description for repo623ssh -p 23231 localhost repo description icecream "This is a new description"624625# Hide repo from listing626ssh -p 23231 localhost repo hidden icecream true627628# List repository info (branches, tags, description, etc)629ssh -p 23231 localhost repo icecream info630```631632To make a repository private, use `repo private <repo> [true|false]`. Private633repos can only be accessed by admins and collaborators.634635```sh636ssh -p 23231 localhost repo private icecream true637```638639### Repository Branches & Tags640641Use `repo branch` and `repo tag` to list, and delete branches or tags. You can642also use `repo branch default` to set or get the repository default branch.643644### Repository Tree645646To print a file tree for the project, just use the `repo tree` command along with647the repo name as the SSH command to your Soft Serve server:648649```sh650ssh -p 23231 localhost repo tree soft-serve651```652653You can also specify the sub-path and a specific reference or branch.654655```sh656ssh -p 23231 localhost repo tree soft-serve server/config657ssh -p 23231 localhost repo tree soft-serve main server/config658```659660From there, you can print individual files using the `repo blob` command:661662```sh663ssh -p 23231 localhost repo blob soft-serve cmd/soft/main.go664```665666You can add the `-c` flag to enable syntax coloring and `-l` to print line667numbers:668669```sh670ssh -p 23231 localhost repo blob soft-serve cmd/soft/main.go -c -l671672```673674Use `--raw` to print raw file contents. This is useful for dumping binary data.675676### Repository webhooks677678Soft Serve supports repository webhooks using the `repo webhook` command. You679can create and manage webhooks for different repository events such as _push_,680_collaborators_, and _branch_tag_create_ events.681682```683Manage repository webhooks684685Usage:686 ssh -p 23231 localhost repo webhook [command]687688Aliases:689 webhook, webhooks690691Available Commands:692 create Create a repository webhook693 delete Delete a repository webhook694 deliveries Manage webhook deliveries695 list List repository webhooks696 update Update a repository webhook697698Flags:699 -h, --help help for webhook700```701702## The Soft Serve TUI703704<img src="https://stuff.charm.sh/soft-serve/soft-serve-demo-commit.png" width="750" alt="TUI example showing a diff">705706Soft Serve TUI is mainly used to browse repos over SSH. You can also use it to707browse local repositories with `soft browse` or running `soft` within a Git708repository.709710```sh711ssh localhost -p 23231712```713714It's also possible to “link” to a specific repo:715716```sh717ssh -p 23231 localhost -t soft-serve718```719720You can copy text to your clipboard over SSH. For instance, you can press721<kbd>c</kbd> on the highlighted repo in the menu to copy the clone command722[^osc52].723724[^osc52]:725 Copying over SSH depends on your terminal support of OSC52. Refer to726 [go-osc52](https://github.com/aymanbagabas/go-osc52) for more information.727728## Hooks729730Soft Serve supports git server-side hooks `pre-receive`, `update`,731`post-update`, and `post-receive`. This means you can define your own hooks to732run on repository push events. Hooks can be defined as a per-repository hook,733and/or global hooks that run for all repositories.734735You can find per-repository hooks under the repository `hooks` directory.736737Globs hooks can be found in your `SOFT_SERVE_DATA_PATH` directory under738`hooks`. Defining global hooks is useful if you want to run CI/CD for example.739740Here's an example of sending a message after receiving a push event. Create an741executable file `<data path>/hooks/update`:742743```sh744#!/bin/sh745#746# An example hook script to echo information about the push747# and send it to the client.748749refname="$1"750oldrev="$2"751newrev="$3"752753# Safety check754if [ -z "$GIT_DIR" ]; then755 echo "Don't run this script from the command line." >&2756 echo " (if you want, you could supply GIT_DIR then run" >&2757 echo " $0 <ref> <oldrev> <newrev>)" >&2758 exit 1759fi760761if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then762 echo "usage: $0 <ref> <oldrev> <newrev>" >&2763 exit 1764fi765766# Check types767# if $newrev is 0000...0000, it's a commit to delete a ref.768zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')769if [ "$newrev" = "$zero" ]; then770 newrev_type=delete771else772 newrev_type=$(git cat-file -t $newrev)773fi774775echo "Hi from Soft Serve update hook!"776echo777echo "RefName: $refname"778echo "Change Type: $newrev_type"779echo "Old SHA1: $oldrev"780echo "New SHA1: $newrev"781782exit 0783```784785Now, you should get a message after pushing changes to any repository.786787## A note about RSA keys788789Unfortunately, due to a shortcoming in Go’s `x/crypto/ssh` package, Soft Serve790does not currently support access via new SSH RSA keys: only the old SHA-1791ones will work.792793Until we sort this out you’ll either need an SHA-1 RSA key or a key with794another algorithm, e.g. Ed25519. Not sure what type of keys you have?795You can check with the following:796797```sh798$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;799```800801If you’re curious about the inner workings of this problem have a look at:802803- https://github.com/golang/go/issues/37278804- https://go-review.googlesource.com/c/crypto/+/220037805- https://github.com/golang/crypto/pull/197806807## Contributing808809See [contributing][contribute].810811[contribute]: https://github.com/charmbracelet/soft-serve/contribute812813## Feedback814815We’d love to hear your thoughts on this project. Feel free to drop us a note!816817- [Twitter](https://twitter.com/charmcli)818- [The Fediverse](https://mastodon.social/@charmcli)819- [Discord](https://charm.sh/chat)820821## License822823[MIT](https://github.com/charmbracelet/soft-serve/raw/main/LICENSE)824825---826827Part of [Charm](https://charm.sh).828829<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>830831Charm热爱开源 • Charm loves open source