Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Store repository secrets encrypted, with a CLI and a settings form

0c40a1d3b48eb0ade37334f62ee526c371ecb61c · Keenan Nemetz · 7d ago · parent 122cbe4f

Store repository secrets encrypted, with a CLI and a settings form

A repository has values that cannot live in its code: a deploy key, a
registry token, a staging password. They are now stored in data/secrets.db,
sealed with AES-256-GCM under data/secrets.key, and read through one store
that records every read and every change. The key is a separate file, and
secrets_key can move it, so a backup of the data directory can be a backup of
ciphertext rather than of plaintext.

`vole secret list|set|get|rm` asks the instance over the key the caller
already pushes with, since the store is server-side. set reads the value from
stdin or a file and never from the argument list; get writes the value and
nothing else. The maintainer role is the capability: the accounts that approve
work and allow CI own the credentials those jobs will use.

The repository's Settings tab gains the same list — names, versions, who
changed one, when a value was last read — with set, replace and delete. It
never renders a value, and a maintainer who is not an admin now reaches that
page for this section alone.

Values are limited to what one environment variable can carry: 128 KiB for
NAME=value, valid UTF-8 and no NUL. A longer value could never reach a step,
and the refusal says so.

Slice 1 of #154.

client/remote.go +12

@@ -3,6 +3,7 @@ package client
3 3 import (
4 4 "errors"
5 5 "fmt"
6 "io"
6 7 "net/url"
7 8 "os"
8 9 "os/exec"
@@ -148,7 +149,15 @@ func SSHTarget(gitDir, remote string) (host, port, path string, err error) {
148 149 // RunSSH runs one of the instance's read-only verbs over the key the caller
149 150 // already pushes with. No new endpoint, no second credential.
150 151 func RunSSH(host, port, verb string) (string, error) {
152 return runSSH(host, port, verb, nil)
153 }
154
155 // runSSH is RunSSH with an optional stdin. The secret verbs carry their value
156 // this way: an exec line is visible to every process on both hosts, and the
157 // instance keeps the command it ran.
158 func runSSH(host, port, verb string, stdin io.Reader) (string, error) {
151 159 cmd := SSHCommand(host, port, verb)
160 cmd.Stdin = stdin
152 161 var out, stderr strings.Builder
153 162 cmd.Stdout, cmd.Stderr = &out, &stderr
154 163 if err := cmd.Run(); err != nil {
@@ -159,6 +168,9 @@ func RunSSH(host, port, verb string) (string, error) {
159 168 }
160 169 return "", fmt.Errorf("%s: %w: %s", host, err, strings.TrimSpace(stderr.String()))
161 170 }
171 // The output is returned as it arrived. A verb that answers with a value must
172 // not have a byte trimmed off it, and a verb that answers with lines has its
173 // whitespace handled by the caller that knows what the lines are.
162 174 return out.String(), nil
163 175 }
164 176