aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/mbf
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-06-07 17:15:27 +0900
committerOphestra <cat@gensokyo.uk>2026-06-07 19:25:11 +0900
commite8bb5a622d0c1895b1768a90d0b422ec084db73f (patch)
tree46011917ee77bc70c2f6d473b85cee24dfe3e152 /cmd/mbf
parent22e508fe17cdf6be6240e6b73ac5b90bc6bfd77e (diff)
internal/rosa: mirror service via external cache
This provides an authenticated implementation of the external cache. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd/mbf')
-rw-r--r--cmd/mbf/cache.go19
-rw-r--r--cmd/mbf/main.go82
2 files changed, 100 insertions, 1 deletions
diff --git a/cmd/mbf/cache.go b/cmd/mbf/cache.go
index 0452a3e1..b683326f 100644
--- a/cmd/mbf/cache.go
+++ b/cmd/mbf/cache.go
@@ -2,6 +2,7 @@ package main
import (
"context"
+ "net/http"
"os"
"path/filepath"
"testing"
@@ -9,6 +10,7 @@ import (
"hakurei.app/check"
"hakurei.app/container"
"hakurei.app/internal/pkg"
+ "hakurei.app/internal/rosa"
"hakurei.app/message"
)
@@ -30,7 +32,7 @@ type cache struct {
// Loaded artifact of [rosa.QEMU].
qemu pkg.Artifact
- base string
+ base, mirror string
}
// open opens the underlying [pkg.Cache].
@@ -86,6 +88,21 @@ func (cache *cache) open() (err error) {
}
done <- struct{}{}
+ if cache.mirror != "" {
+ var pub []byte
+ pub, err = os.ReadFile(base.Append("ed25519.pub").String())
+ if err != nil {
+ cache.c.Close()
+ return
+ }
+ var r rosa.Remote
+ if r, err = rosa.NewRemote(cache.mirror, pub, http.DefaultClient); err != nil {
+ cache.c.Close()
+ return err
+ }
+ cache.c.SetExternal(r)
+ }
+
if cache.qemu != nil {
var pathname *check.Absolute
pathname, _, err = cache.c.Cure(cache.qemu)
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go
index 86496f98..ae835500 100644
--- a/cmd/mbf/main.go
+++ b/cmd/mbf/main.go
@@ -14,6 +14,7 @@ package main
import (
"context"
+ "crypto/ed25519"
"crypto/sha512"
"errors"
"fmt"
@@ -47,6 +48,19 @@ import (
"hakurei.app/cmd/mbf/internal/pkgserver/ui"
)
+// writeFileExcl is like [os.WriteFile], but sets [os.O_EXCL] instead.
+func writeFileExcl(name string, data []byte, perm os.FileMode) error {
+ f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+ if err != nil {
+ return err
+ }
+ _, err = f.Write(data)
+ if err1 := f.Close(); err1 != nil && err == nil {
+ err = err1
+ }
+ return err
+}
+
func main() {
container.TryArgv0(nil)
@@ -187,6 +201,10 @@ func main() {
"d", command.StringFlag("$MBF_CACHE_DIR"),
"Directory to store cured artifacts",
).Flag(
+ &cm.mirror,
+ "r", command.StringFlag("$MBF_REMOTE"),
+ "URL of mirror service",
+ ).Flag(
&cm.idle,
"sched-idle", command.BoolFlag(false),
"Set SCHED_IDLE scheduling policy",
@@ -462,6 +480,70 @@ func main() {
},
)
+ c.NewCommand(
+ "keygen",
+ "Create keypair for local cache",
+ func([]string) error {
+ pub, priv, err := ed25519.GenerateKey(nil)
+ if err != nil {
+ return err
+ }
+
+ return errors.Join(writeFileExcl(filepath.Join(
+ cm.base,
+ "ed25519.pub",
+ ), pub, 0444), writeFileExcl(filepath.Join(
+ cm.base,
+ "ed25519",
+ ), priv, 0400))
+ },
+ )
+
+ c.NewCommand(
+ "serve",
+ "Export local cache as mirror",
+ func(args []string) error {
+ const shutdownTimeout = 15 * time.Second
+
+ if len(args) != 1 {
+ return errors.New("serve requires 1 argument")
+ }
+
+ var key ed25519.PrivateKey
+ if p, err := os.ReadFile(filepath.Join(cm.base, "ed25519")); err != nil {
+ return err
+ } else if len(p) != ed25519.PrivateKeySize {
+ return errors.New("invalid private key")
+ } else {
+ key = p
+ }
+
+ var h http.Handler
+ if base, err := os.OpenRoot(cm.base); err != nil {
+ return err
+ } else {
+ h = rosa.NewMirror(msg, base, key)
+ }
+
+ server := http.Server{Addr: args[0], Handler: h}
+ go func() {
+ <-ctx.Done()
+ cc, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
+ defer cancel()
+ if err := server.Shutdown(cc); err != nil {
+ log.Fatal(err)
+ }
+ }()
+
+ msg.Verbosef("listening on %q", args[0])
+ err := server.ListenAndServe()
+ if errors.Is(err, http.ErrServerClosed) {
+ err = nil
+ }
+ return err
+ },
+ )
+
{
var (
flagGentoo string