From 5f06617be20ea98a6760dd830b50bdab5f102525 Mon Sep 17 00:00:00 2001 From: Christopher Brady Date: Thu, 7 May 2026 09:15:46 -0600 Subject: [PATCH 1/2] add headers to websocket connection --- handshake.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 1 + 2 files changed, 63 insertions(+) create mode 100644 handshake.go diff --git a/handshake.go b/handshake.go new file mode 100644 index 0000000..a2ad076 --- /dev/null +++ b/handshake.go @@ -0,0 +1,62 @@ +package main + +import ( + "net/http" + "runtime/debug" + "sync" +) + +const ( + clientName = "schematic-datastream-replicator" + clientModulePath = "github.com/schematichq/schematic-datastream-replicator" +) + +var ( + clientVersionOnce sync.Once + clientVersion string +) + +// ClientVersion returns the module version of the replicator recorded in the +// build. For tagged releases this returns the tag (e.g. v1.2.3). For untagged +// or replace-directive builds it returns "(devel)"; if build info is +// unavailable, "unknown". +func ClientVersion() string { + clientVersionOnce.Do(func() { + clientVersion = resolveClientVersion() + }) + return clientVersion +} + +func resolveClientVersion() string { + info, ok := debug.ReadBuildInfo() + if !ok { + return "unknown" + } + if info.Main.Path == clientModulePath && info.Main.Version != "" { + return info.Main.Version + } + for _, dep := range info.Deps { + if dep == nil || dep.Path != clientModulePath { + continue + } + if dep.Replace != nil && dep.Replace.Version != "" { + return dep.Replace.Version + } + if dep.Version != "" { + return dep.Version + } + } + return "unknown" +} + +// datastreamHandshakeHeaders returns the HTTP headers attached to the +// outbound DataStream WebSocket handshake so the backend can distinguish +// replicator connections from direct-SDK connections and correlate them to a +// release. +func datastreamHandshakeHeaders() http.Header { + h := http.Header{} + h.Set("X-Schematic-Datastream-Mode", "replicator") + h.Set("X-Schematic-Client", clientName) + h.Set("X-Schematic-Client-Version", ClientVersion()) + return h +} diff --git a/main.go b/main.go index 8d9e260..6997f39 100644 --- a/main.go +++ b/main.go @@ -286,6 +286,7 @@ func main() { MaxReconnectAttempts: 10, MinReconnectDelay: 1 * time.Second, MaxReconnectDelay: 30 * time.Second, + ExtraHeaders: datastreamHandshakeHeaders(), } // Get WebSocket ping/pong intervals from environment (optional) From db4a8f56f3d7215dd5b5b0ef71ef9371b357e812 Mon Sep 17 00:00:00 2001 From: Christopher Brady Date: Wed, 13 May 2026 12:46:00 -0600 Subject: [PATCH 2/2] populate ldflag variables --- handshake.go | 12 ++++++++---- main.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/handshake.go b/handshake.go index a2ad076..6673f00 100644 --- a/handshake.go +++ b/handshake.go @@ -16,10 +16,11 @@ var ( clientVersion string ) -// ClientVersion returns the module version of the replicator recorded in the -// build. For tagged releases this returns the tag (e.g. v1.2.3). For untagged -// or replace-directive builds it returns "(devel)"; if build info is -// unavailable, "unknown". +// ClientVersion returns the replicator version reported on the DataStream +// handshake. It prefers the ldflag-injected `main.version` used by the +// Dockerfile build, then falls back to module build info (a tag for +// `go install …@v1.2.3` builds, "(devel)" for local `go build .`), and +// finally "unknown". func ClientVersion() string { clientVersionOnce.Do(func() { clientVersion = resolveClientVersion() @@ -28,6 +29,9 @@ func ClientVersion() string { } func resolveClientVersion() string { + if version != "" { + return version + } info, ok := debug.ReadBuildInfo() if !ok { return "unknown" diff --git a/main.go b/main.go index 6997f39..87440d5 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,20 @@ import ( "github.com/schematichq/schematic-go/option" ) +// Populated at build time via -ldflags="-X main.version=... -X main.commit=... -X main.buildTime=..." +var ( + version string + commit string + buildTime string +) + +func valueOrUnknown(s string) string { + if s == "" { + return "unknown" + } + return s +} + const ( defaultAPIURL = "https://api.schematichq.com" apiKeyEnvVar = "SCHEMATIC_API_KEY" @@ -310,6 +324,7 @@ func main() { } logger.Info(context.Background(), "Starting Schematic Datastream Replicator...") + logger.Info(context.Background(), fmt.Sprintf("Version: %s, Commit: %s, Build time: %s", ClientVersion(), valueOrUnknown(commit), valueOrUnknown(buildTime))) logger.Info(context.Background(), fmt.Sprintf("API URL: %s", apiBaseURL)) if os.Getenv("SCHEMATIC_DATASTREAM_URL") != "" { logger.Info(context.Background(), fmt.Sprintf("Datastream URL: %s (explicit)", datastreamURL))