Git backed by object storage because you can't stop me
git object-storage kefka
10

Configure Feed

Select the types of activity you want to include in your feed.

feat(git-protocol): route git:// through the auth.Authorizer

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso (May 28, 2026, 11:01 PM EDT) 8d677e13 1bfac32c

+31 -4
+2
cmd/objgitd/example_hook_test.go
··· 13 13 14 14 "github.com/go-git/go-billy/v6/memfs" 15 15 "github.com/go-git/go-git/v6/plumbing/transport" 16 + "tangled.org/xeiaso.net/objgit/internal/auth" 16 17 ) 17 18 18 19 // TestExampleHookRuns pushes the repository's own example hook ··· 38 39 d := &daemon{ 39 40 fs: fs, 40 41 loader: transport.NewFilesystemLoader(fs, false), 42 + authz: auth.AllowAnonymous{AllowWrite: true}, 41 43 allowPush: true, 42 44 allowHooks: true, 43 45 hookTimeout: 30 * time.Second,
+21 -4
cmd/objgitd/git_protocol.go
··· 21 21 "github.com/go-git/go-git/v6/plumbing/transport" 22 22 "github.com/go-git/go-git/v6/storage" 23 23 "github.com/go-git/go-git/v6/storage/filesystem" 24 + "tangled.org/xeiaso.net/objgit/internal/auth" 24 25 ) 25 26 26 27 // handshakeTimeout bounds how long a client has to send its git-proto-request. ··· 43 44 storage.Storer 44 45 } 45 46 47 + // operationFor maps a git service to the access it needs: receive-pack writes, 48 + // everything else (upload-pack, upload-archive) reads. 49 + func operationFor(service string) auth.Operation { 50 + if service == transport.ReceivePackService { 51 + return auth.Write 52 + } 53 + return auth.Read 54 + } 55 + 46 56 // daemon serves the git:// (TCP) protocol out of a billy filesystem. 47 57 type daemon struct { 48 58 fs billy.Filesystem 49 59 loader transport.Loader 60 + authz auth.Authorizer 50 61 allowPush bool 51 62 52 63 // allowHooks gates running .objgit/hooks/receive-pack after a push. ··· 113 124 // writer is the raw conn: its final Close() ends the connection. 114 125 r := io.NopCloser(conn) 115 126 127 + if d.authz.Authorize(ctx, auth.Request{ 128 + Repo: req.Pathname, 129 + Operation: operationFor(req.RequestCommand), 130 + Cred: auth.Anonymous{}, 131 + Transport: "git", 132 + }) != auth.Allow { 133 + _, _ = pktline.WriteError(conn, fmt.Errorf("access denied")) 134 + return fmt.Errorf("access denied for %q (%s)", req.Pathname, req.RequestCommand) 135 + } 136 + 116 137 switch req.RequestCommand { 117 138 case transport.UploadPackService: 118 139 st, err := d.loader.Load(&url.URL{Path: req.Pathname}) ··· 133 154 return transport.UploadArchive(ctx, st, r, conn, &transport.UploadArchiveRequest{}) 134 155 135 156 case transport.ReceivePackService: 136 - if !d.allowPush { 137 - _, _ = pktline.WriteError(conn, fmt.Errorf("push is disabled on this server")) 138 - return fmt.Errorf("push rejected for %q", req.Pathname) 139 - } 140 157 st, err := d.loadOrInit(req.Pathname) 141 158 if err != nil { 142 159 _, _ = pktline.WriteError(conn, fmt.Errorf("cannot open repository %q", req.Pathname))
+3
cmd/objgitd/git_protocol_test.go
··· 12 12 13 13 "github.com/go-git/go-billy/v6/memfs" 14 14 "github.com/go-git/go-git/v6/plumbing/transport" 15 + "tangled.org/xeiaso.net/objgit/internal/auth" 15 16 ) 16 17 17 18 // TestDaemonPushCreatesRepo reproduces "git push git://host/new.git" against a ··· 26 27 d := &daemon{ 27 28 fs: fs, 28 29 loader: transport.NewFilesystemLoader(fs, false), 30 + authz: auth.AllowAnonymous{AllowWrite: true}, 29 31 allowPush: true, 30 32 } 31 33 ··· 83 85 d := &daemon{ 84 86 fs: fs, 85 87 loader: transport.NewFilesystemLoader(fs, false), 88 + authz: auth.AllowAnonymous{AllowWrite: false}, 86 89 allowPush: false, 87 90 } 88 91
+3
cmd/objgitd/hooks_test.go
··· 16 16 "github.com/go-git/go-billy/v6/memfs" 17 17 "github.com/go-git/go-git/v6/plumbing" 18 18 "github.com/go-git/go-git/v6/plumbing/transport" 19 + "tangled.org/xeiaso.net/objgit/internal/auth" 19 20 ) 20 21 21 22 func TestDiffRefs(t *testing.T) { ··· 107 108 d := &daemon{ 108 109 fs: fs, 109 110 loader: transport.NewFilesystemLoader(fs, false), 111 + authz: auth.AllowAnonymous{AllowWrite: true}, 110 112 allowPush: true, 111 113 allowHooks: true, 112 114 hookTimeout: 30 * time.Second, ··· 184 186 d := &daemon{ 185 187 fs: fs, 186 188 loader: transport.NewFilesystemLoader(fs, false), 189 + authz: auth.AllowAnonymous{AllowWrite: true}, 187 190 allowPush: true, 188 191 allowHooks: true, 189 192 hookTimeout: 30 * time.Second,
+2
cmd/objgitd/main.go
··· 18 18 "github.com/tigrisdata/storage-go" 19 19 "golang.org/x/sync/errgroup" 20 20 "tangled.org/xeiaso.net/objgit/internal" 21 + "tangled.org/xeiaso.net/objgit/internal/auth" 21 22 "tangled.org/xeiaso.net/objgit/internal/s3fs" 22 23 23 24 _ "github.com/joho/godotenv/autoload" ··· 73 74 d := &daemon{ 74 75 fs: fsys, 75 76 loader: transport.NewFilesystemLoader(fsys, false), 77 + authz: auth.AllowAnonymous{AllowWrite: *allowPush}, 76 78 allowPush: *allowPush, 77 79 allowHooks: *allowHooks, 78 80 hookTimeout: *hookTimeout,