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
14 changes: 14 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ builds:
- arm64
ldflags:
- -s -w
- id: sam-one
main: ./cmd/sam-one
binary: sam-one
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
ldflags:
- -s -w
- id: nano-init
main: .
# nano-init is its own module: it carries a userspace TCP stack, and that
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.sam-console
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ FROM gcr.io/distroless/static-debian12:nonroot

WORKDIR /app
COPY --from=builder --chown=nonroot:nonroot /bin/sam-console /usr/local/bin/sam-console
COPY --chown=nonroot:nonroot cmd/sam-console/public /app/public
COPY --chown=nonroot:nonroot internal/console/public /app/public

USER nonroot:nonroot
EXPOSE 8081
Expand Down
23 changes: 23 additions & 0 deletions Dockerfile.sam-one
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Stage 1: Build
FROM golang:1.27.0 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -o sam-one ./cmd/sam-one
# Pre-create the data dir owned by the nonroot UID so that a fresh
# named/anonymous volume mounted over it inherits writable ownership instead
# of defaulting to root.
RUN mkdir -p /data-seed && chown 65532:65532 /data-seed

# Stage 2: Final
FROM gcr.io/distroless/static:nonroot
WORKDIR /tmp
COPY --from=builder --chown=nonroot:nonroot /app/sam-one /
COPY --from=builder --chown=nonroot:nonroot /data-seed /data
USER nonroot:nonroot
ENTRYPOINT ["/sam-one"]
# Default args only: a container needs a stable published port, and overriding
# the command line must re-point --data-dir at a writable mount or the sqlite
# db lands on the container filesystem.
CMD ["--data-dir", "/data", "--port", "8080"]
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ build:
go build -v -o "$(OUT_DIR)/sam-node" ./cmd/sam-node
go build -v -o "$(OUT_DIR)/sam-control-plane" ./cmd/sam-control-plane
go build -v -o "$(OUT_DIR)/sam-router" ./cmd/sam-router
go build -v -o "$(OUT_DIR)/sam-one" ./cmd/sam-one
go build -v -o "$(OUT_DIR)/mcp-client" ./cmd/mcp-client
go build -v -o "$(OUT_DIR)/sam-box" ./cmd/sam-box
go build -v -o "$(OUT_DIR)/sam-bench" ./cmd/sam-bench
Expand Down
3 changes: 2 additions & 1 deletion cmd/sam-console/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func main() {
controlPlaneURL = flag.String("control-plane", "http://localhost:8080", "URL of the SAM control plane")
adminTokenPath = flag.String("admin-token-path", "", "Path to file containing the admin token (or env SAM_ADMIN_TOKEN)")
bindAddr = flag.String("bind-addr", ":8081", "Address to bind the console server")
staticDir = flag.String("static-dir", "public", "Directory containing static frontend files")
staticDir = flag.String("static-dir", "", "Directory containing static frontend files (default: assets embedded in the binary)")
basePath = flag.String("base-path", "", "Base path prefix for the console (e.g. /console)")
externalURL = flag.String("external-url", "", "Origin browsers reach this console on, e.g. https://console.example. Sets the OIDC redirect_uri and cookie Secure flag instead of trusting the Host and X-Forwarded-Proto headers")
)
Expand All @@ -51,6 +51,7 @@ func main() {
ControlPlaneURL: *controlPlaneURL,
AdminToken: adminToken,
StaticDir: *staticDir,
StaticFS: console.EmbeddedAssets(),
BasePath: console.NormalizeBasePath(*basePath),
ExternalURL: *externalURL,
})
Expand Down
6 changes: 5 additions & 1 deletion cmd/sam-node/daemonize.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ func daemonizeRun(socketPath string) error {
return err
}
if !enrolled && bootstrapTokenFlag == "" && bootstrapTokenPathFlag == "" && jwtFlag == "" && jwtPathFlag == "" {
target := controlPlane
if target == "" {
target = "<control-plane-url>"
}
return fmt.Errorf("this node is not enrolled yet, and enrolling needs a one-time login you have to approve:\n"+
" sam-node join --headless %s\n"+
"then re-run 'sam-node run --daemonize'", controlPlane)
"then re-run 'sam-node run --daemonize'", target)
}

tokenArgs, tokenPath, err := ensureDaemonToken(dataDir)
Expand Down
72 changes: 12 additions & 60 deletions cmd/sam-node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,56 +137,17 @@ func isInteractiveTerminal() bool {
return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)
}

// Public community meshes a node can join without any private control plane
// of its own. Neither is the default without explicit user confirmation.
const (
publicTestnetControlPlane = "https://bananas.sam-mesh.dev" // open to anyone, deployed from the tip of main
publicProductionControlPlane = "https://hub.sam-mesh.dev" // open to anyone, deployed from the latest release tag
)

// defaultControlPlane resolves which control plane to use when none was
// explicitly passed: the previously stored one, or the public testnet.
// explicitly passed: the previously stored one, or "" when this node has
// never been pointed at a mesh (joining then requires --control-plane).
func defaultControlPlane(store *node.Store, explicit string) string {
if explicit != "" {
return explicit
}
if h, err := store.LoadControlPlaneURL(); err == nil && h != "" {
return h
}
return publicTestnetControlPlane
}

// choosePublicMesh explains what bananas.sam-mesh.dev and hub.sam-mesh.dev
// are and asks which (if either) to join, since a node must never join a
// public mesh without the user's explicit ack; passing --control-plane <url>
// is the silent, explicit alternative. Returns the chosen URL, or "" if the
// user declined (the default).
func choosePublicMesh() string {
fmt.Printf(
"No control plane specified. Join a public community mesh?\n"+
" 1) %s - open to anyone, deployed from the tip of main (may be unstable)\n"+
" 2) %s - open to anyone, deployed from the latest release tag\n"+
"Choice [1/2] (default: don't join): ",
publicTestnetControlPlane, publicProductionControlPlane)
reader := bufio.NewReader(os.Stdin)
response, err := reader.ReadString('\n')
if err != nil {
return ""
}
return parseMeshChoice(response)
}

// parseMeshChoice maps a raw prompt answer to the chosen public mesh's URL,
// or "" for anything other than an explicit "1" or "2".
func parseMeshChoice(response string) string {
switch strings.TrimSpace(response) {
case "1":
return publicTestnetControlPlane
case "2":
return publicProductionControlPlane
default:
return ""
}
return ""
}

// isYesResponse reports whether a raw prompt answer is an explicit "y"/"yes"
Expand Down Expand Up @@ -219,9 +180,9 @@ const (
// joinNeedsConfirmSwitch: --control-plane conflicts with the stored
// mesh; an interactive terminal must confirm resetting and rejoining.
joinNeedsConfirmSwitch
// joinNeedsChooseMesh: no explicit or stored control plane; an
// interactive terminal must pick a public mesh (or decline).
joinNeedsChooseMesh
// joinNeedsControlPlane: no explicit or stored control plane; the
// user must pass --control-plane for the mesh this node should join.
joinNeedsControlPlane
// joinProceed: enough is known to go straight to interactiveJoin.
joinProceed
)
Expand All @@ -245,7 +206,7 @@ func decideJoinAction(identityExists, hasPubKey, interactive bool, controlPlaneA
case mismatched:
return joinNeedsConfirmSwitch
case controlPlaneAddr == "" && stored == "":
return joinNeedsChooseMesh
return joinNeedsControlPlane
default:
return joinProceed
}
Expand Down Expand Up @@ -457,16 +418,12 @@ func main() {
logger.Fatalf("Failed to reset stored identity: %v", err)
}
fallthrough
case joinNeedsChooseMesh, joinProceed:
case joinNeedsControlPlane, joinProceed:
if !mismatched && identityExists && len(controlPlanePubKey) == 0 {
logger.Warn("Stored identity is missing its control plane public key; re-joining")
}
if controlPlaneAddr == "" && stored == "" {
chosen := choosePublicMesh()
if chosen == "" {
logger.Fatal("Aborted: no control plane specified.")
}
controlPlaneAddr = chosen
logger.Fatal("No control plane specified: pass --control-plane <url> for the mesh this node should join.")
}
targetControlPlane := normalizeControlPlaneURL(defaultControlPlane(store, controlPlaneAddr))
jwtStr, controlPlaneInfo, err = interactiveJoin(ctx, store, targetControlPlane)
Expand Down Expand Up @@ -573,7 +530,7 @@ func main() {
if len(routerAddrs) > 0 {
initRouterAddrs = routerAddrs
} else {
logger.Fatalf("Invalid control plane address and no stored config: %v. You can use community maintained meshes like hub.sam-mesh.dev (Production) or bananas.sam-mesh.dev (Testnet)", err)
logger.Fatalf("Invalid control plane address and no stored config: %v. Pass --control-plane <url> for the mesh this node should join", err)
}
}
}
Expand Down Expand Up @@ -724,12 +681,7 @@ func main() {
}

if targetControlPlane == "" {
chosen := choosePublicMesh()
if chosen == "" {
fmt.Println("Aborting join operation.")
return
}
targetControlPlane = chosen
logger.Fatal("No control plane specified: sam-node join <control-plane-url>")
}

targetControlPlane = normalizeControlPlaneURL(targetControlPlane)
Expand Down Expand Up @@ -899,7 +851,7 @@ func main() {
runCmd.Flags().StringSliceVar(&listenAddrs, "listen", []string{"/ip4/0.0.0.0/udp/5001/quic-v1", "/ip4/0.0.0.0/tcp/5002"}, "libp2p Listen Addrs")
runCmd.Flags().StringVar(&jwtFlag, "jwt", "", "Pre-fetched JWT token")
runCmd.Flags().StringVar(&jwtPathFlag, "jwt-path", "", "Path to file containing JWT token")
runCmd.Flags().BoolVar(&joinFlag, "join", false, "Enroll interactively on first run if no identity exists yet (defaults to the public testnet unless --control-plane is set); a no-op on later restarts")
runCmd.Flags().BoolVar(&joinFlag, "join", false, "Enroll interactively on first run if no identity exists yet (requires --control-plane or a previously stored mesh); a no-op on later restarts")
runCmd.Flags().StringVar(&bootstrapTokenFlag, "bootstrap-token", "", "Pre-shared bootstrap token for enrollment")
runCmd.Flags().StringVar(&bootstrapTokenPathFlag, "bootstrap-token-path", "", "Path to file containing the bootstrap token (recommended over --bootstrap-token)")
runCmd.Flags().StringVar(&clientIDFlag, "client-id", "", "OIDC Client ID for M2M")
Expand Down
25 changes: 3 additions & 22 deletions cmd/sam-node/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ func TestDefaultControlPlane(t *testing.T) {
if got := defaultControlPlane(store, "https://example.com"); got != "https://example.com" {
t.Errorf("explicit control plane should win: got %q", got)
}
if got := defaultControlPlane(store, ""); got != publicTestnetControlPlane {
t.Errorf("no explicit and no stored URL should default to the public testnet: got %q", got)
if got := defaultControlPlane(store, ""); got != "" {
t.Errorf("no explicit and no stored URL should resolve to nothing, never a public mesh: got %q", got)
}

if err := store.SaveControlPlaneURL("https://stored.example.com"); err != nil {
Expand Down Expand Up @@ -136,25 +136,6 @@ func TestIsYesResponse(t *testing.T) {
}
}

func TestParseMeshChoice(t *testing.T) {
tests := map[string]string{
"1": publicTestnetControlPlane,
"1\n": publicTestnetControlPlane,
" 1 \n": publicTestnetControlPlane,
"2": publicProductionControlPlane,
"2\n": publicProductionControlPlane,
"3": "",
"": "",
"\n": "",
"y": "",
}
for in, want := range tests {
if got := parseMeshChoice(in); got != want {
t.Errorf("parseMeshChoice(%q) = %q, want %q", in, got, want)
}
}
}

func TestDecideJoinAction(t *testing.T) {
const a, b = "https://a.example.com", "https://b.example.com"

Expand All @@ -164,7 +145,7 @@ func TestDecideJoinAction(t *testing.T) {
controlPlaneAddr, stored string
want joinAction
}{
{"no identity, interactive, no urls: choose mesh", false, false, true, "", "", joinNeedsChooseMesh},
{"no identity, interactive, no urls: needs control plane", false, false, true, "", "", joinNeedsControlPlane},
{"no identity, interactive, explicit url: proceed", false, false, true, a, "", joinProceed},
{"no identity, non-interactive: fall back", false, false, false, "", "", joinFallbackNoTTY},
{"usable identity: skip regardless of terminal", true, true, false, "", a, joinSkip},
Expand Down
Loading
Loading