Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ This is the logical successor to the value provided by [trillian-examples](https

Projects that are currently underway are:
- [Verifiable Index](./vindex/): a verifiable index (map) built on top of a single Input Log
- [SumDB to tlog-tiles proxy](./sumdb/): a proxy to make Go's SumDB available with a tlog-tiles API
28 changes: 28 additions & 0 deletions sumdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## SumDB to tlog-tiles proxy

This is a proxy that serves the [Go SumDB](https://sum.golang.org/) with a [tlog-tiles](https://c2sp.org/tlog-tiles) API.
This allows tooling written for the tlog-tiles API to be used with the SumDB, even though its API is slightly different.

### Running

```shell
go run ./sumdb/proxy.go --listen=":8089"
```

### Using

Any valid tlog-tiles API paths sent to the listen address will be routed to the SumDB proxy.
Paths will be changed as necessary, and leaf data returned will be rewritten to comply with the tlog-tiles spec.

For example, to run Tessera's tlog-tiles `fsck` tool against the log to confirm integrity:

```shell
# Put the SumDB public key in a file so that fsck can read it.
echo sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8 > ~/.go.sum.vkey

# Run the fsck tool against the proxy.
go run github.com/transparency-dev/tessera/cmd/fsck@main \
--storage_url=http://localhost:8089/ \
--public_key ~/.go.sum.vkey \
--origin "go.sum database tree"
```
99 changes: 99 additions & 0 deletions sumdb/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2025 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// sumdb is a command that launches a local proxy that allows clients to query
// using the tlog-tiles API, and retrieve results from SumDB.
package main

import (
"bytes"
"encoding/binary"
"flag"
"fmt"

"net/http"
"net/http/httputil"
"net/url"
"strings"

"io"

"k8s.io/klog/v2"
)

var (
listen = flag.String("listen", ":8089", "Address to set up HTTP server listening on")
)

const (
upstreamBase = "https://sum.golang.org"
)

func main() {
klog.InitFlags(nil)
flag.Parse()

upstream, err := url.Parse(upstreamBase)
if err != nil {
klog.Fatalf("Failed to parse upstream URL %q: %v", upstreamBase, err)
}

const tlogEntriesPrefix = "/tile/entries/"
const tlogTilePrefix = "/tile/"

const sumDBTileDataPrefix = "/tile/8/data/"
const sumDBTilePrefix = "/tile/8/"
proxy := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(upstream)
klog.V(2).Infof("Request for %s", r.In.URL.Path)
if r.In.URL.Path == "/checkpoint" {
r.Out.URL.Path = "/latest"
} else if strings.HasPrefix(r.In.URL.Path, tlogEntriesPrefix) {
o := strings.TrimPrefix(r.In.URL.Path, tlogEntriesPrefix)
r.Out.URL.Path = fmt.Sprintf("%s%s", sumDBTileDataPrefix, o)
} else if strings.HasPrefix(r.In.URL.Path, tlogTilePrefix) {
o := strings.TrimPrefix(r.In.URL.Path, tlogTilePrefix)
r.Out.URL.Path = fmt.Sprintf("%s%s", sumDBTilePrefix, o)
}
},
ModifyResponse: func(r *http.Response) error {
if strings.HasPrefix(r.Request.URL.Path, sumDBTileDataPrefix) {
// Leaf data requires splitting into individual records, and then
// reassembling with the record size prepended to each record.
data, err := io.ReadAll(r.Body)
if err != nil {
return err
}
leaves := bytes.Split(data, []byte{'\n', '\n'})
buf := bytes.Buffer{}
for _, l := range leaves {
r := append(bytes.TrimSpace(l), '\n')
size := binary.BigEndian.AppendUint16(nil, uint16(len(r)))
buf.Write(size)
buf.Write(r)
}
r.Body = io.NopCloser(&buf)
r.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
}
return nil
},
}

klog.Infof("Proxying tlog-tiles API to %s on %s", upstreamBase, *listen)
if err := http.ListenAndServe(*listen, proxy); err != nil {
klog.Fatalf("ListenAndServe: %v", err)
}
}

Loading