From dc78339745976eceb3a82941684627e825960391 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:18:35 +0800 Subject: [PATCH 01/32] test: require native HIP manifest target --- cli/rec_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 00e95b8..17364ea 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -355,6 +355,25 @@ func TestManifestSchemaAndPaths(t *testing.T) { } } +func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { + m := Manifest{ + Engine: "flint-native", Schema: 1, Model: "flint", + Revision: "3b93f9053b530488405ae9fca51d4900d6ce21f0", + OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, + MinRAM: 1, Entry: "config.json", + Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, + RuntimeSHA: digest([]byte("runtime")), + RuntimeFiles: []File{{Name: "flint-serve", Size: 1, SHA256: digest([]byte("r")), Executable: true}}, + } + got, err := decodeManifest(mustJSON(t, m)) + if err != nil { + t.Fatal(err) + } + if got.Engine != "flint-native" || got.Backend != "hip" { + t.Fatalf("native HIP target identity lost: %+v", got) + } +} + func TestAppleChipAllowlist(t *testing.T) { for chip, ok := range map[string]bool{"Apple M4 Pro": true, "Apple M5 Pro": true, "Apple M5 Max": true, "Apple M4 Max": false, "Apple M4": false, "Apple M1 Pro": false} { if (checkAppleChip(chip) == nil) != ok { From 9ab812c8360fab3215d21dad90c3e8734328c611 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:19:04 +0800 Subject: [PATCH 02/32] feat: admit native HIP Flint bundles --- cli/manifest.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/cli/manifest.go b/cli/manifest.go index c193378..0397de0 100644 --- a/cli/manifest.go +++ b/cli/manifest.go @@ -69,6 +69,17 @@ func safeRuntimeName(s string) bool { const runtimeServer = "rec-mlx" +func runtimeServerFor(m Manifest) (string, error) { + switch { + case m.Engine == "mlx" && m.OS == "darwin" && m.Arch == "arm64" && m.Backend == "metal": + return runtimeServer, nil + case m.Engine == "flint-native" && m.OS == "linux" && m.Arch == "amd64" && m.Backend == "hip": + return "flint-serve", nil + default: + return "", fmt.Errorf("unsupported engine or target") + } +} + func decodeManifest(data []byte) (Manifest, error) { var m Manifest d := json.NewDecoder(bytes.NewReader(data)) @@ -80,9 +91,10 @@ func decodeManifest(data []byte) (Manifest, error) { if d.Decode(&extra) != io.EOF { return m, fmt.Errorf("trailing manifest data") } - // Each engine is one target. Adding an engine adds its own case here. - if m.Engine != "mlx" || m.OS != "darwin" || m.Arch != "arm64" || m.Backend != "metal" { - return m, fmt.Errorf("unsupported engine or target: rec runs the MLX engine on Apple Silicon") + // Each engine is one closed target tuple. + serverName, err := runtimeServerFor(m) + if err != nil { + return m, err } if m.MaxContext < 0 || (m.MaxContext > 0 && m.Context > m.MaxContext) { return m, fmt.Errorf("invalid maximum context") @@ -124,12 +136,12 @@ func decodeManifest(data []byte) (Manifest, error) { } server := false for _, f := range m.RuntimeFiles { - if f.Name == runtimeServer && f.Executable { + if f.Name == serverName && f.Executable { server = true } } if !server { - return m, fmt.Errorf("runtime requires executable %s", runtimeServer) + return m, fmt.Errorf("runtime requires executable %s", serverName) } return m, nil } From 9930771b83afafa5402e298fa2063d862d455888 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:20:20 +0800 Subject: [PATCH 03/32] test: require native HIP launch contract --- cli/context_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cli/context_test.go b/cli/context_test.go index 1d7a4b8..7d39643 100644 --- a/cli/context_test.go +++ b/cli/context_test.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "os" + "reflect" "testing" ) @@ -97,6 +98,26 @@ func TestContextReachesEngine(t *testing.T) { } } +func TestNativeHIPLaunchContract(t *testing.T) { + m := releaseManifest(t) + m.Engine, m.OS, m.Arch, m.Backend = "flint-native", "linux", "amd64", "hip" + m.Context = 262144 + executable, args, env := launchCommand(Paths{Config: "/config"}, m, "/runtime", "/model/config.json", 11500) + wantArgs := []string{ + "--model", "/model", "--checkpoint-dir", "/model", + "--context-capacity", "262144", "--device-budget", "32592542688", + "--bind", "127.0.0.1", "--port", "11500", "--queue-limit", "8", + "--api-key-file", "/config/api-key", "--served-model", "flint", + "--enforce-tool-schemas", + } + if executable != "/runtime/flint-serve" || !reflect.DeepEqual(args, wantArgs) { + t.Fatalf("native launch mismatch\nexecutable: %q\nargs: %#v", executable, args) + } + if len(env) != 0 { + t.Fatalf("native launch must not place credentials in the environment: %#v", env) + } +} + func releaseManifest(t *testing.T) Manifest { t.Helper() b, err := os.ReadFile("../packaging/flint-int4-mlx.json") From 769f037515cb8db7aa2409b7d08a242bc1bcdf1b Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:20:43 +0800 Subject: [PATCH 04/32] feat: launch native HIP Flint runtime --- cli/engine.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cli/engine.go b/cli/engine.go index f0b7938..d5dbc3a 100644 --- a/cli/engine.go +++ b/cli/engine.go @@ -7,6 +7,21 @@ import ( ) func launchCommand(p Paths, m Manifest, runtimeDir, model string, port int) (string, []string, []string) { + if m.Engine == "flint-native" { + const base = uint64(25074426720) + const perToken = uint64(14 * 2048) + const logits = uint64(150144 * 13) + budget := base + uint64(m.Context-1)*perToken + logits + modelDir := filepath.Dir(model) + return filepath.Join(runtimeDir, "flint-serve"), []string{ + "--model", modelDir, "--checkpoint-dir", modelDir, + "--context-capacity", strconv.Itoa(m.Context), + "--device-budget", strconv.FormatUint(budget, 10), + "--bind", "127.0.0.1", "--port", strconv.Itoa(port), + "--queue-limit", "8", "--api-key-file", filepath.Join(p.Config, "api-key"), + "--served-model", m.Model, "--enforce-tool-schemas", + }, nil + } return filepath.Join(runtimeDir, runtimeServer), []string{ "serve", m.Model, "--model-dir", filepath.Dir(model), "--manifest", model, "--port", strconv.Itoa(port), "--context-limit", strconv.Itoa(m.Context), From b349bf814374f85a2a8e021067ac8cdd62b429dc Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:26:26 +0800 Subject: [PATCH 05/32] test: scaffold local model verification coverage --- cli/rec_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 17364ea..938fa26 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -374,6 +374,35 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { } } +func TestVerifyLocalModelUsesVerifiedFilesInPlace(t *testing.T) { + root := t.TempDir() + data := []byte("pinned checkpoint bytes") + entry := filepath.Join(root, "config.json") + if err := os.WriteFile(entry, data, 0600); err != nil { + t.Fatal(err) + } + m := Manifest{Entry: "config.json", Files: []File{{ + Name: "config.json", Size: int64(len(data)), SHA256: digest(data), + }}} + got, err := verifyLocalModel(root, m, io.Discard) + if err != nil || got != entry { + t.Fatalf("verified local model not returned in place: %q %v", got, err) + } + if err := os.WriteFile(entry, []byte("corrupt checkpoint bytes"), 0600); err != nil { + t.Fatal(err) + } + if _, err := verifyLocalModel(root, m, io.Discard); err == nil { + t.Fatal("corrupt local model accepted") + } + os.Remove(entry) + if err := os.Symlink(filepath.Join(root, "target"), entry); err != nil { + t.Fatal(err) + } + if _, err := verifyLocalModel(root, m, io.Discard); err == nil { + t.Fatal("symlinked local model file accepted") + } +} + func TestAppleChipAllowlist(t *testing.T) { for chip, ok := range map[string]bool{"Apple M4 Pro": true, "Apple M5 Pro": true, "Apple M5 Max": true, "Apple M4 Max": false, "Apple M4": false, "Apple M1 Pro": false} { if (checkAppleChip(chip) == nil) != ok { From 2c561fade266613f6c7783162f35fd650bafce36 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:28:40 +0800 Subject: [PATCH 06/32] feat: verify and serve local native checkpoints --- cli/cli.go | 7 ++++++- cli/download.go | 31 +++++++++++++++++++++++++++++++ cli/rec_test.go | 2 +- cli/serve.go | 9 +++++++-- cli/serve_test.go | 6 +++--- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 49e42a6..c2aade0 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -92,10 +92,12 @@ func run(args []string) error { manifest := flags.String("manifest", "", "") runtimeDir := flags.String("runtime-dir", "", "") var contextTokens int + var modelDir string port := 11500 if command == "serve" { flags.IntVar(&contextTokens, "context", 0, "") flags.IntVar(&port, "port", port, "") + flags.StringVar(&modelDir, "model-dir", "", "") } flags.SetOutput(io.Discard) if e := flags.Parse(rest); e != nil { @@ -127,6 +129,9 @@ func run(args []string) error { if *runtimeDir != "" && *manifest == "" { return fmt.Errorf("--runtime-dir requires --manifest") } + if modelDir != "" && m.Engine != "flint-native" { + return fmt.Errorf("--model-dir is supported only by the native Flint engine") + } if m.Development { newScreen(os.Stderr).step(warningStyle.Render("!"), "Development bundle: this is not a qualified Flint release.", "") } @@ -139,7 +144,7 @@ func run(args []string) error { } return e } - return serve(ctx, p, m, *runtimeDir, port, os.Stderr, background) + return serve(ctx, p, m, *runtimeDir, modelDir, port, os.Stderr, background) } func doctor(p Paths, m Manifest, manifestErr error) error { diff --git a/cli/download.go b/cli/download.go index 7642544..d59ff81 100644 --- a/cli/download.go +++ b/cli/download.go @@ -137,6 +137,37 @@ func ensureModel(ctx context.Context, p Paths, m Manifest, out io.Writer) (strin return ensureModelWithClient(ctx, p, m, out, client) } +// verifyLocalModel hashes the manifest-declared files in their existing +// directory. It never copies or downloads model bytes; the native runtime +// independently revalidates the pinned checkpoint before GPU admission. +func verifyLocalModel(root string, m Manifest, out io.Writer) (string, error) { + abs, err := filepath.Abs(root) + if err != nil { + return "", err + } + before, err := os.Lstat(abs) + if err != nil || !before.IsDir() || before.Mode()&os.ModeSymlink != 0 { + return "", fmt.Errorf("local model directory must be a real directory") + } + var total int64 + for _, f := range m.Files { + total += f.Size + } + progress := newTransfer(out, "Verifying "+displayName(m.Model), "Verified "+displayName(m.Model), 0, total) + for _, f := range m.Files { + if err := verifyFileInto(filepath.Join(abs, f.Name), f, progress); err != nil { + progress.fail(err.Error()) + return "", err + } + } + after, err := os.Lstat(abs) + if err != nil || !os.SameFile(before, after) { + return "", fmt.Errorf("local model directory changed during verification") + } + progress.succeed() + return filepath.Join(abs, m.Entry), nil +} + func ensureModelWithClient(ctx context.Context, p Paths, m Manifest, out io.Writer, client *http.Client) (string, error) { id := modelDigest(m) root := filepath.Join(p.Data, "models", id) diff --git a/cli/rec_test.go b/cli/rec_test.go index 938fa26..1f4bd0a 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -381,7 +381,7 @@ func TestVerifyLocalModelUsesVerifiedFilesInPlace(t *testing.T) { if err := os.WriteFile(entry, data, 0600); err != nil { t.Fatal(err) } - m := Manifest{Entry: "config.json", Files: []File{{ + m := Manifest{Model: "flint", Entry: "config.json", Files: []File{{ Name: "config.json", Size: int64(len(data)), SHA256: digest(data), }}} got, err := verifyLocalModel(root, m, io.Discard) diff --git a/cli/serve.go b/cli/serve.go index c4def68..e64bf9c 100644 --- a/cli/serve.go +++ b/cli/serve.go @@ -210,7 +210,7 @@ func probe(ctx context.Context, base, key, model string, phase ...func(string)) return nil } -func serve(parent context.Context, p Paths, m Manifest, override string, port int, out io.Writer, background ...bool) error { +func serve(parent context.Context, p Paths, m Manifest, override, modelDir string, port int, out io.Writer, background ...bool) error { l, e := lock(filepath.Join(p.State, "instance.lock")) if e != nil { if s, err := control(p, "status"); err == nil { @@ -273,7 +273,12 @@ func serve(parent context.Context, p Paths, m Manifest, override string, port in return e } runtimeProgress.succeed() - model, e := ensureModel(ctx, p, m, out) + var model string + if modelDir != "" { + model, e = verifyLocalModel(modelDir, m, out) + } else { + model, e = ensureModel(ctx, p, m, out) + } if e != nil { return e } diff --git a/cli/serve_test.go b/cli/serve_test.go index 050d204..83a2131 100644 --- a/cli/serve_test.go +++ b/cli/serve_test.go @@ -95,7 +95,7 @@ func TestServeLifecycle(t *testing.T) { var out lockedBuffer done := make(chan error, 1) - go func() { done <- serve(context.Background(), p, m, runtimeDir, port, &out) }() + go func() { done <- serve(context.Background(), p, m, runtimeDir, "", port, &out) }() deadline := time.Now().Add(2 * time.Minute) var s Status for !s.Ready { @@ -115,12 +115,12 @@ func TestServeLifecycle(t *testing.T) { } var again bytes.Buffer - if err := serve(context.Background(), p, m, runtimeDir, port, &again); err != nil || !strings.Contains(again.String(), "test is already running") { + if err := serve(context.Background(), p, m, runtimeDir, "", port, &again); err != nil || !strings.Contains(again.String(), "test is already running") { t.Fatalf("identical run did not reuse the server: %v %s", err, again.String()) } changed := m changed.Context, changed.MaxContext = 4096, 4096 - if err := serve(context.Background(), p, changed, runtimeDir, port, io.Discard); err == nil { + if err := serve(context.Background(), p, changed, runtimeDir, "", port, io.Discard); err == nil { t.Fatal("a different context reused the running server") } From 937687736bdc1ab707fe568f16b58d4ff13ed8ca Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:29:27 +0800 Subject: [PATCH 07/32] test: require Linux native HIP preflight --- cli/rec_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 1f4bd0a..4dd179e 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -14,6 +14,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "sync" "testing" @@ -411,6 +412,16 @@ func TestAppleChipAllowlist(t *testing.T) { } } +func TestNativeHIPPreflightOnLinuxAMD64(t *testing.T) { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("native HIP target is Linux/amd64") + } + m := Manifest{Engine: "flint-native", OS: "linux", Arch: "amd64", Backend: "hip", MinRAM: 1} + if err := preflight(m); err != nil { + t.Fatalf("native HIP host rejected: %v", err) + } +} + func discardProgress() *transfer { return newTransfer(io.Discard, "Downloading", "Downloaded", 0, 0) } func TestStampRejectsRestoredMtime(t *testing.T) { From 9399462142e0b0984dff834c9a6d96ed11c155d7 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:29:55 +0800 Subject: [PATCH 08/32] feat: preflight Linux native HIP hosts --- cli/platform.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cli/platform.go b/cli/platform.go index 3d50b83..5bdc5ca 100644 --- a/cli/platform.go +++ b/cli/platform.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "os" "os/exec" "runtime" "strconv" @@ -18,6 +19,28 @@ func command(name string, args ...string) (string, error) { } func preflight(m Manifest) error { + if m.Engine == "flint-native" { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + return fmt.Errorf("native Flint requires Linux/amd64") + } + data, err := os.ReadFile("/proc/meminfo") + if err != nil { + return err + } + fields := strings.Fields(string(data)) + if len(fields) < 2 || fields[0] != "MemTotal:" { + return fmt.Errorf("cannot determine host memory") + } + kib, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil || kib <= 0 || kib > (1<<63-1)/1024 { + return fmt.Errorf("cannot determine host memory") + } + ram := kib * 1024 + if ram < m.MinRAM { + return fmt.Errorf("this model requires %d GiB of memory; detected %d GiB", m.MinRAM>>30, ram>>30) + } + return nil + } if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { return fmt.Errorf("rec requires an Apple Silicon Mac") } From 76e12d4abf186b7466e3b54f4b79d9a93e6cbad7 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:33:07 +0800 Subject: [PATCH 09/32] feat: add pinned Linux HIP Flint manifest --- packaging/flint-int4-hip.json | 88 +++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packaging/flint-int4-hip.json diff --git a/packaging/flint-int4-hip.json b/packaging/flint-int4-hip.json new file mode 100644 index 0000000..bbc6c66 --- /dev/null +++ b/packaging/flint-int4-hip.json @@ -0,0 +1,88 @@ +{ + "schema": 1, + "model": "flint", + "revision": "3b93f9053b530488405ae9fca51d4900d6ce21f0", + "development": false, + "engine": "flint-native", + "os": "linux", + "arch": "amd64", + "backend": "hip", + "context": 262144, + "max_context": 262144, + "min_ram_bytes": 51539607552, + "entry": "flint-int4.json", + "files": [ + { + "name": "chat_template.jinja", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/chat_template.jinja", + "size": 2328, + "sha256": "7940f4ffa3275c36caa4a582f845aaad79da11018b3961e8fbcad6ae29733466" + }, + { + "name": "config.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/config.json", + "size": 3082, + "sha256": "80f577a984db1bd40dfff88457b611d4c13e807704a945616be27f28809eed21" + }, + { + "name": "generation_config.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/generation_config.json", + "size": 202, + "sha256": "a499c71895e008abd572558a5ad5904feaed92cf2a3b6e2cb0e9cabf03b96ed3" + }, + { + "name": "model-00000.safetensors", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/model-00000.safetensors", + "size": 4745746152, + "sha256": "1a362742a0136c95ed751e2c56d83e63d4ed9ac674984b23b368f105d16cfe7a" + }, + { + "name": "model-00001.safetensors", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/model-00001.safetensors", + "size": 4682022280, + "sha256": "12979140fa019042ff5cdec4c0237cc87db13b56428ce7fe093dd9d33893ea32" + }, + { + "name": "model-00002.safetensors", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/model-00002.safetensors", + "size": 4682022280, + "sha256": "01805c6b882fdec008b0184ec68e7516102205b521e63ca5126f9d52b9aed87b" + }, + { + "name": "model-00003.safetensors", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/model-00003.safetensors", + "size": 4682022280, + "sha256": "9380ed032d90e0ff566d49672faf292f354b3b0e7320820c150eeed8c75e9d42" + }, + { + "name": "model-00004.safetensors", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/model-00004.safetensors", + "size": 3777099608, + "sha256": "c1ba12d2c2a80feb5e554554a01cfde2b71c2638e27fc0ee597b4121399eff99" + }, + { + "name": "special_tokens_map.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/special_tokens_map.json", + "size": 20192, + "sha256": "be37b302338dda189bc70bfbe902d8faebeb5986d60c1342713439eefc4ca182" + }, + { + "name": "tokenizer.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/tokenizer.json", + "size": 10879128, + "sha256": "a263cce0b7e2b03f7ee07ce809783ef4b47f160c3f268a4ea1524f638da45b68" + }, + { + "name": "tokenizer_config.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/tokenizer_config.json", + "size": 30439, + "sha256": "159ac88217e90ffd0a4a294726048bb302829e82e1958dc85bb203aa79138fbf" + }, + { + "name": "flint-int4.json", + "url": "https://huggingface.co/RecursiveSI/flint-int4/resolve/3b93f9053b530488405ae9fca51d4900d6ce21f0/flint-int4.json", + "size": 1206, + "sha256": "319822f10e32a5916b57e23c7f05f9e92b24d02b3a136d02a3846b5edab78109" + } + ] +} From c0b1a6267b5523bd5390a0dd77483f6e940b78b0 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:34:49 +0800 Subject: [PATCH 10/32] docs: generalize native platform messaging --- cli/cli.go | 4 ++-- cli/help.go | 1 + cli/serve.go | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index c2aade0..e585f5e 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -171,10 +171,10 @@ func doctor(p Paths, m Manifest, manifestErr error) error { return manifestErr } if err := preflight(m); err != nil { - ui.failed("This Mac does not meet the requirements", "") + ui.failed("This system does not meet the requirements", "") return err } - ui.succeeded("This Mac meets the requirements", "") + ui.succeeded("This system meets the requirements", "") if !modelFilesPresent(p, m) { ui.step(warningStyle.Render("!"), displayName(m.Model)+" is not downloaded", "run rec pull") return nil diff --git a/cli/help.go b/cli/help.go index 2bc4474..9b1c723 100644 --- a/cli/help.go +++ b/cli/help.go @@ -27,6 +27,7 @@ var commands = []commandHelp{ {"serve", "[MODEL]", "Start the model server in this terminal", append([][2]string{ {"--context TOKENS", "Total input and output tokens (default: set by the release)"}, {"--port PORT", "Local port (default: 11500)"}, + {"--model-dir DIR", "Verify and use an existing local native checkpoint in place"}, }, developmentOptions...)}, {"status", "", "Show whether the server is running", nil}, {"stop", "", "Stop the server", nil}, diff --git a/cli/serve.go b/cli/serve.go index e64bf9c..76dc092 100644 --- a/cli/serve.go +++ b/cli/serve.go @@ -262,10 +262,10 @@ func serve(parent context.Context, p Paths, m Manifest, override, modelDir strin go func() { _ = srv.Serve(listener) }() ui := newScreen(out) if e = preflight(m); e != nil { - ui.failed("This Mac is not supported", "") + ui.failed("This system is not supported", "") return e } - ui.succeeded("This Mac is supported", "") + ui.succeeded("This system is supported", "") runtimeProgress := startActivity(out, "Preparing runtime", "Runtime ready") runtimeDir, e := ensureRuntime(p, m, override) if e != nil { From d5bc2ca4558f49277cdc93214b13cf8c8486d49b Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:37:18 +0800 Subject: [PATCH 11/32] fix: size HIP host RAM preflight for native loading --- packaging/flint-int4-hip.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/flint-int4-hip.json b/packaging/flint-int4-hip.json index bbc6c66..cfebb3f 100644 --- a/packaging/flint-int4-hip.json +++ b/packaging/flint-int4-hip.json @@ -9,7 +9,7 @@ "backend": "hip", "context": 262144, "max_context": 262144, - "min_ram_bytes": 51539607552, + "min_ram_bytes": 8589934592, "entry": "flint-int4.json", "files": [ { From d32e5a5ea198d79dbf085ede8aeb25f48a42f19e Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:41:09 +0800 Subject: [PATCH 12/32] test: exercise native HIP managed lifecycle --- cli/serve_test.go | 17 +++++++++++------ cli/testdata/fake-rec-mlx | 28 ++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/cli/serve_test.go b/cli/serve_test.go index 83a2131..45f8c69 100644 --- a/cli/serve_test.go +++ b/cli/serve_test.go @@ -49,8 +49,9 @@ func (b *lockedBuffer) String() string { } func TestServeLifecycle(t *testing.T) { - if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { - t.Skip("serving requires Apple Silicon") + if (runtime.GOOS != "darwin" || runtime.GOARCH != "arm64") && + (runtime.GOOS != "linux" || runtime.GOARCH != "amd64") { + t.Skip("serving lifecycle target unavailable") } // Unix socket paths are limited to 104 bytes on macOS; t.TempDir is too long. root, err := os.MkdirTemp("/tmp", "rec-test-") @@ -75,13 +76,17 @@ func TestServeLifecycle(t *testing.T) { if err := os.Mkdir(runtimeDir, 0700); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(runtimeDir, runtimeServer), server, 0700); err != nil { + engine, backend, serverName := "mlx", "metal", runtimeServer + if runtime.GOOS == "linux" { + engine, backend, serverName = "flint-native", "hip", "flint-serve" + } + if err := os.WriteFile(filepath.Join(runtimeDir, serverName), server, 0700); err != nil { t.Fatal(err) } - m := Manifest{Engine: "mlx", Schema: 1, Model: "test", Revision: "test", Development: true, OS: "darwin", Arch: "arm64", Backend: "metal", + m := Manifest{Engine: engine, Schema: 1, Model: "test", Revision: "test", Development: true, OS: runtime.GOOS, Arch: runtime.GOARCH, Backend: backend, Context: 2048, MinRAM: 1, Entry: "config.json", Files: []File{{Name: "config.json", URL: weights.URL + "/config.json", Size: int64(len(config)), SHA256: digest(config)}}, - RuntimeSHA: digest([]byte("runtime")), RuntimeFiles: []File{{Name: runtimeServer, Size: int64(len(server)), SHA256: digest(server), Executable: true}}} + RuntimeSHA: digest([]byte("runtime")), RuntimeFiles: []File{{Name: serverName, Size: int64(len(server)), SHA256: digest(server), Executable: true}}} if _, err := ensureModelWithClient(context.Background(), p, m, io.Discard, weights.Client()); err != nil { t.Fatal(err) } @@ -110,7 +115,7 @@ func TestServeLifecycle(t *testing.T) { time.Sleep(100 * time.Millisecond) s, _ = control(p, "status") } - if !strings.Contains(out.String(), "test is ready.") || s.Endpoint != fmt.Sprintf("http://127.0.0.1:%d/v1", port) || s.Engine != "mlx" { + if !strings.Contains(out.String(), "test is ready.") || s.Endpoint != fmt.Sprintf("http://127.0.0.1:%d/v1", port) || s.Engine != engine { t.Fatalf("wrong readiness report %+v:\n%s", s, out.String()) } diff --git a/cli/testdata/fake-rec-mlx b/cli/testdata/fake-rec-mlx index 8a56828..b25acf4 100755 --- a/cli/testdata/fake-rec-mlx +++ b/cli/testdata/fake-rec-mlx @@ -6,14 +6,30 @@ import json from pathlib import Path parser = argparse.ArgumentParser() -parser.add_argument("command", choices=("serve",)) -parser.add_argument("model") -for flag in ("--model-dir", "--manifest", "--api-key-file"): - parser.add_argument(flag, type=Path, required=True) +native = len(__import__('sys').argv) > 1 and __import__('sys').argv[1].startswith('--') +if native: + for flag in ("--model", "--checkpoint-dir", "--api-key-file"): + parser.add_argument(flag, type=Path, required=True) + parser.add_argument("--context-capacity", type=int, required=True) + parser.add_argument("--device-budget", required=True) + parser.add_argument("--bind", required=True) + parser.add_argument("--queue-limit", required=True) + parser.add_argument("--served-model", required=True) + parser.add_argument("--enforce-tool-schemas", action="store_true") +else: + parser.add_argument("command", choices=("serve",)) + parser.add_argument("model") + for flag in ("--model-dir", "--manifest", "--api-key-file"): + parser.add_argument(flag, type=Path, required=True) + parser.add_argument("--context-limit", type=int, required=True) parser.add_argument("--port", type=int, required=True) -parser.add_argument("--context-limit", type=int, required=True) args = parser.parse_args() -assert (args.model_dir / args.manifest.name).is_file() +if native: + assert args.checkpoint_dir == args.model + args.model = args.served_model + assert args.model +else: + assert (args.model_dir / args.manifest.name).is_file() key = args.api_key_file.read_text().strip() From 9949f8c506c284d953e9eaa7164372761bf66809 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:44:48 +0800 Subject: [PATCH 13/32] build: stage native Flint runtime for REC packaging --- packaging/build-flint-runtime.py | 48 ++++++++++++++++++++++++++++++++ packaging/package.py | 2 +- tests/package_test.py | 24 ++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 packaging/build-flint-runtime.py diff --git a/packaging/build-flint-runtime.py b/packaging/build-flint-runtime.py new file mode 100644 index 0000000..1478b74 --- /dev/null +++ b/packaging/build-flint-runtime.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +"""Stage a relocatable native Flint executable for REC packaging.""" + +import argparse +import json +import shutil +import stat +import subprocess +from pathlib import Path + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--flint-serve", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + + source = args.flint_serve + info = source.lstat() + if source.is_symlink() or not stat.S_ISREG(info.st_mode) or not info.st_mode & 0o111: + parser.error("--flint-serve must be an executable regular file, not a symlink") + probe = subprocess.run( + [str(source.resolve()), "--help"], capture_output=True, text=True, timeout=30 + ) + if probe.returncode != 0 or "flint-serve" not in probe.stdout or "--checkpoint-dir" not in probe.stdout: + parser.error("executable does not satisfy the native Flint server contract") + + output = args.output.resolve() + if output.exists(): + if output.is_symlink() or not output.is_dir(): + parser.error("--output must be a directory, not a symlink") + shutil.rmtree(output) + output.mkdir(parents=True, mode=0o700) + destination = output / "flint-serve" + shutil.copyfile(source, destination) + destination.chmod(0o755) + (output / "build.json").write_text( + json.dumps( + {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"}, + indent=2, + ) + + "\n" + ) + print(output) + + +if __name__ == "__main__": + main() diff --git a/packaging/package.py b/packaging/package.py index 78adce8..6433b7c 100644 --- a/packaging/package.py +++ b/packaging/package.py @@ -57,7 +57,7 @@ def main(): "--runtime-dir", type=Path, required=True, - help="runtime directory from packaging/build-mlx-runtime.py", + help="prebuilt runtime directory matching the manifest target", ) p.add_argument( "--manifest", diff --git a/tests/package_test.py b/tests/package_test.py index 6d9f18b..0fbd9f2 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -56,4 +56,28 @@ subprocess.check_output([str(output), "--version"], text=True).strip() == "rec 0.1.0-test" ) + fake_flint = work / "source-flint-serve" + fake_flint.write_text( + "#!/bin/sh\n" + "if [ \"$1\" = --help ]; then echo 'flint-serve --checkpoint-dir'; exit 0; fi\n" + "exit 1\n" + ) + fake_flint.chmod(0o755) + hip_runtime = work / "hip-runtime" + subprocess.run( + [sys.executable, str(root / "packaging/build-flint-runtime.py"), + "--flint-serve", str(fake_flint), "--output", str(hip_runtime)], + check=True, + ) + identity = json.loads((hip_runtime / "build.json").read_text()) + assert identity == {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"} + assert (hip_runtime / "flint-serve").stat().st_mode & 0o111 + bad = work / "flint-link" + bad.symlink_to(fake_flint) + rejected = subprocess.run( + [sys.executable, str(root / "packaging/build-flint-runtime.py"), + "--flint-serve", str(bad), "--output", str(work / "bad-runtime")], + capture_output=True, text=True, + ) + assert rejected.returncode != 0 and "symlink" in rejected.stderr, rejected.stderr print("Packaging, archive validation and runtime/manifest mismatch rejection passed") From 2d60fe0eb751b2b27971afd2ac0296285d34cbb7 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:45:41 +0800 Subject: [PATCH 14/32] docs: document native HIP packaging and serving --- README.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ab01e5f..375f9e3 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,35 @@ curl -fsSL https://raw.githubusercontent.com/recursive-oss/rec/main/install.sh | This installs `rec` in `~/.local/bin` and adds it to your PATH for new terminals. All other data lives under `~/.local/share/rec`. +### Native AMD/HIP development package + +The native HIP target currently requires Linux/amd64, a ROCm installation, +and a locally persisted, verified Flint checkpoint. It does not download or +copy the checkpoint. Build `flint-serve`, then stage and package it with: + +```bash +python3 packaging/build-flint-runtime.py \ + --flint-serve /path/to/flint-serve \ + --output /path/to/flint-runtime +python3 packaging/package.py \ + --runtime-dir /path/to/flint-runtime \ + --manifest packaging/flint-int4-hip.json \ + --version VERSION \ + --output /path/to/rec +``` + +Start the packaged binary against the persistent checkpoint directory: + +```bash +/path/to/rec serve flint \ + --model-dir /path/to/flint-int4 \ + --context 4096 +``` + +The HIP target currently serves the OpenAI-compatible endpoint. Anthropic +Messages support remains specific to the MLX runtime until native support is +implemented. + ## Usage @@ -99,17 +128,19 @@ sessions; remove those variables to keep yours. To send a request directly to the OpenAI-compatible API: ```bash -curl -fsS http://127.0.0.1:11500/v1/chat/completions \ - -H "Authorization: Bearer $(rec auth token)" \ +read -r REC_TOKEN < <(rec auth token) +printf 'header = "Authorization: Bearer %s"\n' "$REC_TOKEN" | \ +curl --config - -fsS http://127.0.0.1:11500/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{"model":"flint","messages":[{"role":"user","content":"Say hello."}],"max_tokens":128}' +unset REC_TOKEN ``` The response contains generated text in `choices`. For other clients, use base URL `http://127.0.0.1:11500/v1`, model `flint`, and the API key from `rec auth token`. -The Anthropic-compatible API is available at +For the MLX runtime, the Anthropic-compatible API is available at `http://127.0.0.1:11500/v1/messages`. Point `ANTHROPIC_BASE_URL` at `http://127.0.0.1:11500` and send the token from `rec auth token` as a bearer token or as `x-api-key`. The model is `flint`. From c715d3999b723fa7c9122149da3132684d648d1e Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:49:03 +0800 Subject: [PATCH 15/32] test: require gfx1151 native HIP hardware --- cli/rec_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 4dd179e..144d442 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -422,6 +422,34 @@ func TestNativeHIPPreflightOnLinuxAMD64(t *testing.T) { } } +func TestNativeHIPHardwareRequiresGFX1151(t *testing.T) { + root := t.TempDir() + kfd := filepath.Join(root, "kfd") + if err := os.WriteFile(kfd, nil, 0600); err != nil { + t.Fatal(err) + } + nodes := filepath.Join(root, "nodes") + if err := os.MkdirAll(filepath.Join(nodes, "1"), 0700); err != nil { + t.Fatal(err) + } + properties := filepath.Join(nodes, "1", "properties") + write := func(target string) { + t.Helper() + content := "simd_count 80\nwave_front_size 32\ngfx_target_version " + target + "\n" + if err := os.WriteFile(properties, []byte(content), 0600); err != nil { + t.Fatal(err) + } + } + write("110501") + if err := nativeHIPHardware(kfd, filepath.Join(nodes, "*", "properties")); err != nil { + t.Fatalf("gfx1151 rejected: %v", err) + } + write("110500") + if err := nativeHIPHardware(kfd, filepath.Join(nodes, "*", "properties")); err == nil { + t.Fatal("non-gfx1151 device accepted") + } +} + func discardProgress() *transfer { return newTransfer(io.Discard, "Downloading", "Downloaded", 0, 0) } func TestStampRejectsRestoredMtime(t *testing.T) { From 45b1b7322bfd7ad3c0f8d4a4abb894af4ae744b2 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 13:51:01 +0800 Subject: [PATCH 16/32] feat: require gfx1151 for native HIP serving --- cli/platform.go | 39 ++++++++++++++++++++++++++++++++++++++- cli/rec_test.go | 9 ++++++--- cli/serve_test.go | 5 +++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/cli/platform.go b/cli/platform.go index 5bdc5ca..62de79d 100644 --- a/cli/platform.go +++ b/cli/platform.go @@ -5,12 +5,49 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "runtime" "strconv" "strings" "time" ) +var nativeHIPProbe = func() error { + return nativeHIPHardware("/dev/kfd", "/sys/class/kfd/kfd/topology/nodes/*/properties") +} + +func nativeHIPHardware(kfdPath, topologyPattern string) error { + kfd, err := os.OpenFile(kfdPath, os.O_RDWR, 0) + if err != nil { + return fmt.Errorf("native Flint requires read/write access to /dev/kfd: %w", err) + } + kfd.Close() + properties, err := filepath.Glob(topologyPattern) + if err != nil || len(properties) == 0 { + return fmt.Errorf("cannot inspect AMD GPU topology") + } + for _, path := range properties { + data, readErr := os.ReadFile(path) + if readErr != nil { + continue + } + values := map[string]string{} + for _, line := range strings.Split(string(data), "\n") { + fields := strings.Fields(line) + if len(fields) == 2 { + values[fields[0]] = fields[1] + } + } + if values["gfx_target_version"] == "110501" && values["wave_front_size"] == "32" { + simd, parseErr := strconv.ParseUint(values["simd_count"], 10, 32) + if parseErr == nil && simd > 0 { + return nil + } + } + } + return fmt.Errorf("native Flint requires an AMD gfx1151 GPU") +} + func command(name string, args ...string) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -39,7 +76,7 @@ func preflight(m Manifest) error { if ram < m.MinRAM { return fmt.Errorf("this model requires %d GiB of memory; detected %d GiB", m.MinRAM>>30, ram>>30) } - return nil + return nativeHIPProbe() } if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { return fmt.Errorf("rec requires an Apple Silicon Mac") diff --git a/cli/rec_test.go b/cli/rec_test.go index 144d442..5f9fc8d 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -360,10 +360,10 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { m := Manifest{ Engine: "flint-native", Schema: 1, Model: "flint", Revision: "3b93f9053b530488405ae9fca51d4900d6ce21f0", - OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, + OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, MinRAM: 1, Entry: "config.json", - Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, - RuntimeSHA: digest([]byte("runtime")), + Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, + RuntimeSHA: digest([]byte("runtime")), RuntimeFiles: []File{{Name: "flint-serve", Size: 1, SHA256: digest([]byte("r")), Executable: true}}, } got, err := decodeManifest(mustJSON(t, m)) @@ -416,6 +416,9 @@ func TestNativeHIPPreflightOnLinuxAMD64(t *testing.T) { if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { t.Skip("native HIP target is Linux/amd64") } + oldProbe := nativeHIPProbe + nativeHIPProbe = func() error { return nil } + defer func() { nativeHIPProbe = oldProbe }() m := Manifest{Engine: "flint-native", OS: "linux", Arch: "amd64", Backend: "hip", MinRAM: 1} if err := preflight(m); err != nil { t.Fatalf("native HIP host rejected: %v", err) diff --git a/cli/serve_test.go b/cli/serve_test.go index 45f8c69..6abdd69 100644 --- a/cli/serve_test.go +++ b/cli/serve_test.go @@ -53,6 +53,11 @@ func TestServeLifecycle(t *testing.T) { (runtime.GOOS != "linux" || runtime.GOARCH != "amd64") { t.Skip("serving lifecycle target unavailable") } + if runtime.GOOS == "linux" { + oldProbe := nativeHIPProbe + nativeHIPProbe = func() error { return nil } + defer func() { nativeHIPProbe = oldProbe }() + } // Unix socket paths are limited to 104 bytes on macOS; t.TempDir is too long. root, err := os.MkdirTemp("/tmp", "rec-test-") if err != nil { From 9a18a3a3656a80f96b7512a46ca4c8c324a426b8 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:09:51 +0800 Subject: [PATCH 17/32] test: require local-only native checkpoints --- cli/rec_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 5f9fc8d..1c032b5 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -412,6 +412,22 @@ func TestAppleChipAllowlist(t *testing.T) { } } +func TestNativeHIPRequiresLocalModelDirectory(t *testing.T) { + native := Manifest{Engine: "flint-native"} + if err := validateModelSource("serve", native, ""); err == nil { + t.Fatal("native serve accepted without --model-dir") + } + if err := validateModelSource("pull", native, ""); err == nil { + t.Fatal("native pull accepted remote checkpoint transport") + } + if err := validateModelSource("serve", native, "/models/flint"); err != nil { + t.Fatalf("native local model rejected: %v", err) + } + if err := validateModelSource("serve", Manifest{Engine: "mlx"}, ""); err != nil { + t.Fatalf("MLX download behavior changed: %v", err) + } +} + func TestNativeHIPPreflightOnLinuxAMD64(t *testing.T) { if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { t.Skip("native HIP target is Linux/amd64") From 2706ed272cf3857f910a35d7ffffb4581bab3241 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:11:46 +0800 Subject: [PATCH 18/32] fix: enforce local-only native checkpoints --- cli/cli.go | 16 ++++++++++++++++ cli/serve.go | 8 +++++++- cli/serve_test.go | 13 +++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index e585f5e..fc0062f 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -13,6 +13,19 @@ import ( "syscall" ) +func validateModelSource(command string, m Manifest, modelDir string) error { + if m.Engine != "flint-native" { + return nil + } + if command == "pull" { + return fmt.Errorf("native Flint checkpoints are local-only; rec pull is unavailable") + } + if command == "serve" && modelDir == "" { + return fmt.Errorf("native Flint requires --model-dir DIR") + } + return nil +} + func run(args []string) error { background := len(args) > 0 && args[0] == "_background" if background { @@ -132,6 +145,9 @@ func run(args []string) error { if modelDir != "" && m.Engine != "flint-native" { return fmt.Errorf("--model-dir is supported only by the native Flint engine") } + if e := validateModelSource(command, m, modelDir); e != nil { + return e + } if m.Development { newScreen(os.Stderr).step(warningStyle.Render("!"), "Development bundle: this is not a qualified Flint release.", "") } diff --git a/cli/serve.go b/cli/serve.go index 76dc092..492aa21 100644 --- a/cli/serve.go +++ b/cli/serve.go @@ -274,9 +274,15 @@ func serve(parent context.Context, p Paths, m Manifest, override, modelDir strin } runtimeProgress.succeed() var model string - if modelDir != "" { + if m.Engine == "flint-native" { + if modelDir == "" { + return fmt.Errorf("native Flint requires --model-dir DIR") + } model, e = verifyLocalModel(modelDir, m, out) } else { + if modelDir != "" { + return fmt.Errorf("--model-dir is supported only by the native Flint engine") + } model, e = ensureModel(ctx, p, m, out) } if e != nil { diff --git a/cli/serve_test.go b/cli/serve_test.go index 6abdd69..72ff727 100644 --- a/cli/serve_test.go +++ b/cli/serve_test.go @@ -92,9 +92,14 @@ func TestServeLifecycle(t *testing.T) { Context: 2048, MinRAM: 1, Entry: "config.json", Files: []File{{Name: "config.json", URL: weights.URL + "/config.json", Size: int64(len(config)), SHA256: digest(config)}}, RuntimeSHA: digest([]byte("runtime")), RuntimeFiles: []File{{Name: serverName, Size: int64(len(server)), SHA256: digest(server), Executable: true}}} - if _, err := ensureModelWithClient(context.Background(), p, m, io.Discard, weights.Client()); err != nil { + modelPath, err := ensureModelWithClient(context.Background(), p, m, io.Discard, weights.Client()) + if err != nil { t.Fatal(err) } + modelDir := "" + if engine == "flint-native" { + modelDir = filepath.Dir(modelPath) + } weights.Close() listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { @@ -105,7 +110,7 @@ func TestServeLifecycle(t *testing.T) { var out lockedBuffer done := make(chan error, 1) - go func() { done <- serve(context.Background(), p, m, runtimeDir, "", port, &out) }() + go func() { done <- serve(context.Background(), p, m, runtimeDir, modelDir, port, &out) }() deadline := time.Now().Add(2 * time.Minute) var s Status for !s.Ready { @@ -125,12 +130,12 @@ func TestServeLifecycle(t *testing.T) { } var again bytes.Buffer - if err := serve(context.Background(), p, m, runtimeDir, "", port, &again); err != nil || !strings.Contains(again.String(), "test is already running") { + if err := serve(context.Background(), p, m, runtimeDir, modelDir, port, &again); err != nil || !strings.Contains(again.String(), "test is already running") { t.Fatalf("identical run did not reuse the server: %v %s", err, again.String()) } changed := m changed.Context, changed.MaxContext = 4096, 4096 - if err := serve(context.Background(), p, changed, runtimeDir, "", port, io.Discard); err == nil { + if err := serve(context.Background(), p, changed, runtimeDir, modelDir, port, io.Discard); err == nil { t.Fatal("a different context reused the running server") } From 708ee92a667e308b5f5d7719fb5e671a9a17c7a3 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:12:10 +0800 Subject: [PATCH 19/32] test: preserve existing runtime output directories --- tests/package_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/package_test.py b/tests/package_test.py index 0fbd9f2..1f117d4 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -72,6 +72,14 @@ identity = json.loads((hip_runtime / "build.json").read_text()) assert identity == {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"} assert (hip_runtime / "flint-serve").stat().st_mode & 0o111 + sentinel = hip_runtime / "keep-me" + sentinel.write_text("do not delete\n") + rejected = subprocess.run( + [sys.executable, str(root / "packaging/build-flint-runtime.py"), + "--flint-serve", str(fake_flint), "--output", str(hip_runtime)], + capture_output=True, text=True, + ) + assert rejected.returncode != 0 and sentinel.read_text() == "do not delete\n", rejected.stderr bad = work / "flint-link" bad.symlink_to(fake_flint) rejected = subprocess.run( From 1f3a5f379aa4439f935ceb828aa0c824f5dbd6c8 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:12:55 +0800 Subject: [PATCH 20/32] fix: refuse existing runtime staging outputs --- packaging/build-flint-runtime.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packaging/build-flint-runtime.py b/packaging/build-flint-runtime.py index 1478b74..e230ba1 100644 --- a/packaging/build-flint-runtime.py +++ b/packaging/build-flint-runtime.py @@ -25,11 +25,10 @@ def main(): if probe.returncode != 0 or "flint-serve" not in probe.stdout or "--checkpoint-dir" not in probe.stdout: parser.error("executable does not satisfy the native Flint server contract") - output = args.output.resolve() - if output.exists(): - if output.is_symlink() or not output.is_dir(): - parser.error("--output must be a directory, not a symlink") - shutil.rmtree(output) + requested_output = args.output + if requested_output.exists() or requested_output.is_symlink(): + parser.error("--output must not already exist") + output = requested_output.resolve() output.mkdir(parents=True, mode=0o700) destination = output / "flint-serve" shutil.copyfile(source, destination) From 681d37b827126d4883ab0e53b1bdfd1e07523185 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:14:11 +0800 Subject: [PATCH 21/32] test: pin native checkpoint and context contract --- cli/rec_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cli/rec_test.go b/cli/rec_test.go index 1c032b5..67598ed 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -360,7 +360,7 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { m := Manifest{ Engine: "flint-native", Schema: 1, Model: "flint", Revision: "3b93f9053b530488405ae9fca51d4900d6ce21f0", - OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, + OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, MaxContext: 262144, MinRAM: 1, Entry: "config.json", Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, RuntimeSHA: digest([]byte("runtime")), @@ -373,6 +373,19 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { if got.Engine != "flint-native" || got.Backend != "hip" { t.Fatalf("native HIP target identity lost: %+v", got) } + for name, change := range map[string]func(*Manifest){ + "revision": func(value *Manifest) { value.Revision = "unpinned" }, + "max context": func(value *Manifest) { value.MaxContext = 262145 }, + "context": func(value *Manifest) { value.Context, value.MaxContext = 262145, 262145 }, + } { + t.Run(name, func(t *testing.T) { + bad := m + change(&bad) + if _, err := decodeManifest(mustJSON(t, bad)); err == nil { + t.Fatal("unpinned native contract accepted") + } + }) + } } func TestVerifyLocalModelUsesVerifiedFilesInPlace(t *testing.T) { From 3fe75f1048de648cbd6d38834bb122c571654cea Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:14:57 +0800 Subject: [PATCH 22/32] fix: bind native manifests to Flint runtime limits --- cli/manifest.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/manifest.go b/cli/manifest.go index 0397de0..488bb8c 100644 --- a/cli/manifest.go +++ b/cli/manifest.go @@ -68,6 +68,8 @@ func safeRuntimeName(s string) bool { } const runtimeServer = "rec-mlx" +const nativeFlintRevision = "3b93f9053b530488405ae9fca51d4900d6ce21f0" +const nativeFlintMaxContext = 262144 func runtimeServerFor(m Manifest) (string, error) { switch { @@ -99,6 +101,10 @@ func decodeManifest(data []byte) (Manifest, error) { if m.MaxContext < 0 || (m.MaxContext > 0 && m.Context > m.MaxContext) { return m, fmt.Errorf("invalid maximum context") } + if m.Engine == "flint-native" && + (m.Revision != nativeFlintRevision || m.MaxContext != nativeFlintMaxContext || m.Context > nativeFlintMaxContext) { + return m, fmt.Errorf("native Flint revision or context does not match the runtime contract") + } if m.Schema != 1 || !safeName(m.Model) || m.Revision == "" || m.Context < 256 || m.MinRAM <= 0 || len(m.Files) == 0 { return m, fmt.Errorf("incomplete model descriptor") } From 54665a809e9d7560986fea81840f8b51f46149bf Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:18:05 +0800 Subject: [PATCH 23/32] test: reject unbound native runtime executables --- tests/package_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/package_test.py b/tests/package_test.py index 1f117d4..c6c4fc8 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -63,6 +63,18 @@ "exit 1\n" ) fake_flint.chmod(0o755) + rejected = subprocess.run( + [sys.executable, str(root / "packaging/build-flint-runtime.py"), + "--flint-serve", str(fake_flint), "--output", str(work / "bad-contract-runtime")], + capture_output=True, text=True, + ) + assert rejected.returncode != 0 and "contract" in rejected.stderr, rejected.stderr + fake_flint.write_text( + "#!/bin/sh\n" + "if [ \"$1\" = --help ]; then echo 'flint-serve --checkpoint-dir'; exit 0; fi\n" + "if [ \"$1\" = --runtime-contract ]; then echo '{\"schema\":1,\"engine\":\"flint-native\",\"os\":\"linux\",\"arch\":\"amd64\",\"backend\":\"hip\",\"revision\":\"3b93f9053b530488405ae9fca51d4900d6ce21f0\",\"max_context\":262144,\"arena_base_bytes\":25074426720,\"kv_bytes_per_token\":28672,\"sampler_bytes\":1951872,\"source_commit\":\"0123456789abcdef0123456789abcdef01234567\",\"source_tree\":\"89abcdef0123456789abcdef0123456789abcdef\"}'; exit 0; fi\n" + "exit 1\n" + ) hip_runtime = work / "hip-runtime" subprocess.run( [sys.executable, str(root / "packaging/build-flint-runtime.py"), From e56e09c9fc4b6b168a38bd538ed508828993477f Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:20:30 +0800 Subject: [PATCH 24/32] fix: bind staged runtime to native Flint contract --- packaging/build-flint-runtime.py | 35 +++++++++++++++++++++++++++++++- tests/package_test.py | 8 ++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packaging/build-flint-runtime.py b/packaging/build-flint-runtime.py index e230ba1..c4a30b1 100644 --- a/packaging/build-flint-runtime.py +++ b/packaging/build-flint-runtime.py @@ -2,7 +2,9 @@ """Stage a relocatable native Flint executable for REC packaging.""" import argparse +import hashlib import json +import re import shutil import stat import subprocess @@ -24,6 +26,35 @@ def main(): ) if probe.returncode != 0 or "flint-serve" not in probe.stdout or "--checkpoint-dir" not in probe.stdout: parser.error("executable does not satisfy the native Flint server contract") + contract_probe = subprocess.run( + [str(source.resolve()), "--runtime-contract"], capture_output=True, text=True, timeout=30 + ) + try: + contract = json.loads(contract_probe.stdout) + except (json.JSONDecodeError, TypeError): + contract = None + fixed = { + "schema": 1, + "engine": "flint-native", + "os": "linux", + "arch": "amd64", + "backend": "hip", + "revision": "3b93f9053b530488405ae9fca51d4900d6ce21f0", + "max_context": 262144, + "arena_base_bytes": 25074426720, + "kv_bytes_per_token": 28672, + "sampler_bytes": 1951872, + } + source_keys = {"source_commit", "source_tree", "source_clean"} + if ( + contract_probe.returncode != 0 + or not isinstance(contract, dict) + or set(contract) != set(fixed) | source_keys + or any(contract.get(key) != value for key, value in fixed.items()) + or contract.get("source_clean") is not True + or not all(re.fullmatch(r"[0-9a-f]{40}", str(contract.get(key, ""))) for key in ("source_commit", "source_tree")) + ): + parser.error("executable runtime contract is missing, dirty, or incompatible") requested_output = args.output if requested_output.exists() or requested_output.is_symlink(): @@ -33,9 +64,11 @@ def main(): destination = output / "flint-serve" shutil.copyfile(source, destination) destination.chmod(0o755) + binary_sha256 = hashlib.sha256(destination.read_bytes()).hexdigest() (output / "build.json").write_text( json.dumps( - {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"}, + {**{key: fixed[key] for key in ("engine", "os", "arch", "backend")}, + "binary_sha256": binary_sha256, "runtime_contract": contract}, indent=2, ) + "\n" diff --git a/tests/package_test.py b/tests/package_test.py index c6c4fc8..47a91d3 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import hashlib import json import platform import re @@ -72,7 +73,7 @@ fake_flint.write_text( "#!/bin/sh\n" "if [ \"$1\" = --help ]; then echo 'flint-serve --checkpoint-dir'; exit 0; fi\n" - "if [ \"$1\" = --runtime-contract ]; then echo '{\"schema\":1,\"engine\":\"flint-native\",\"os\":\"linux\",\"arch\":\"amd64\",\"backend\":\"hip\",\"revision\":\"3b93f9053b530488405ae9fca51d4900d6ce21f0\",\"max_context\":262144,\"arena_base_bytes\":25074426720,\"kv_bytes_per_token\":28672,\"sampler_bytes\":1951872,\"source_commit\":\"0123456789abcdef0123456789abcdef01234567\",\"source_tree\":\"89abcdef0123456789abcdef0123456789abcdef\"}'; exit 0; fi\n" + "if [ \"$1\" = --runtime-contract ]; then echo '{\"schema\":1,\"engine\":\"flint-native\",\"os\":\"linux\",\"arch\":\"amd64\",\"backend\":\"hip\",\"revision\":\"3b93f9053b530488405ae9fca51d4900d6ce21f0\",\"max_context\":262144,\"arena_base_bytes\":25074426720,\"kv_bytes_per_token\":28672,\"sampler_bytes\":1951872,\"source_commit\":\"0123456789abcdef0123456789abcdef01234567\",\"source_tree\":\"89abcdef0123456789abcdef0123456789abcdef\",\"source_clean\":true}'; exit 0; fi\n" "exit 1\n" ) hip_runtime = work / "hip-runtime" @@ -82,7 +83,10 @@ check=True, ) identity = json.loads((hip_runtime / "build.json").read_text()) - assert identity == {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"} + target = {key: identity[key] for key in ("engine", "os", "arch", "backend")} + assert target == {"engine": "flint-native", "os": "linux", "arch": "amd64", "backend": "hip"} + assert identity["binary_sha256"] == hashlib.sha256((hip_runtime / "flint-serve").read_bytes()).hexdigest() + assert identity["runtime_contract"]["source_clean"] is True assert (hip_runtime / "flint-serve").stat().st_mode & 0o111 sentinel = hip_runtime / "keep-me" sentinel.write_text("do not delete\n") From 02b4cacff6a59dd2d8dda2d0f9bd245fa6189375 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 14:47:10 +0800 Subject: [PATCH 25/32] test: format native manifest pin table --- cli/rec_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/rec_test.go b/cli/rec_test.go index 67598ed..22062ea 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -374,7 +374,7 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { t.Fatalf("native HIP target identity lost: %+v", got) } for name, change := range map[string]func(*Manifest){ - "revision": func(value *Manifest) { value.Revision = "unpinned" }, + "revision": func(value *Manifest) { value.Revision = "unpinned" }, "max context": func(value *Manifest) { value.MaxContext = 262145 }, "context": func(value *Manifest) { value.Context, value.MaxContext = 262145, 262145 }, } { From 6fab64eb9a7cc2acae1cdab8e19fd5913a17ff68 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:38:41 +0800 Subject: [PATCH 26/32] test: close native launch and packaging trust gaps --- cli/rec_test.go | 9 +++++++++ tests/package_test.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/cli/rec_test.go b/cli/rec_test.go index 22062ea..0a475c5 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -441,6 +441,15 @@ func TestNativeHIPRequiresLocalModelDirectory(t *testing.T) { } } +func TestNativeHIPLaunchDoesNotAutostartWithoutModelDirectory(t *testing.T) { + if err := validateLaunchAutostart(Manifest{Engine: "flint-native"}); err == nil { + t.Fatal("native launch accepted downloader/background autostart path") + } + if err := validateLaunchAutostart(Manifest{Engine: "mlx"}); err != nil { + t.Fatalf("MLX launch autostart was rejected: %v", err) + } +} + func TestNativeHIPPreflightOnLinuxAMD64(t *testing.T) { if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { t.Skip("native HIP target is Linux/amd64") diff --git a/tests/package_test.py b/tests/package_test.py index 47a91d3..78faffd 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -104,4 +104,45 @@ capture_output=True, text=True, ) assert rejected.returncode != 0 and "symlink" in rejected.stderr, rejected.stderr + + # A successful probe must be bound to the exact inode subsequently staged. + mutating = work / "mutating-flint-serve" + contract_json = json.dumps(identity["runtime_contract"], separators=(",", ":")) + mutating.write_text( + "#!/bin/sh\n" + "if [ \"$1\" = --help ]; then echo 'flint-serve --checkpoint-dir'; exit 0; fi\n" + f"if [ \"$1\" = --runtime-contract ]; then echo '{contract_json}'; " + "printf '#!/bin/sh\\necho MUTATED-NO-CONTRACT\\n' > \"$0.new\"; " + "chmod 755 \"$0.new\"; mv \"$0.new\" \"$0\"; exit 0; fi\n" + "exit 1\n" + ) + mutating.chmod(0o755) + bound_runtime = work / "identity-bound-runtime" + subprocess.run( + [sys.executable, str(root / "packaging/build-flint-runtime.py"), + "--flint-serve", str(mutating), "--output", str(bound_runtime)], + check=True, + ) + staged = bound_runtime / "flint-serve" + assert b"MUTATED-NO-CONTRACT" not in staged.read_bytes(), staged.read_bytes() + assert subprocess.run([str(staged), "--runtime-contract"], capture_output=True).returncode == 0 + + # Native packages must not trust a four-field build.json around an arbitrary executable. + hip_manifest = root / "packaging/flint-int4-hip.json" + arbitrary = work / "arbitrary-runtime" + arbitrary.mkdir() + arbitrary_server = arbitrary / "flint-serve" + arbitrary_server.write_text("#!/bin/sh\nexit 0\n") + arbitrary_server.chmod(0o755) + arbitrary_m = json.loads(hip_manifest.read_text()) + (arbitrary / "build.json").write_text( + json.dumps({k: arbitrary_m[k] for k in ("engine", "os", "arch", "backend")}) + ) + rejected = subprocess.run( + [sys.executable, str(root / "packaging/package.py"), + "--runtime-dir", str(arbitrary), "--manifest", str(hip_manifest), + "--version", "0.1.0-test", "--output", str(work / "bad-native-rec")], + capture_output=True, text=True, + ) + assert rejected.returncode != 0 and "runtime contract" in rejected.stderr, rejected.stderr print("Packaging, archive validation and runtime/manifest mismatch rejection passed") From 8f0491ad9047724312133fa0ab4531116ac3c39e Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:42:48 +0800 Subject: [PATCH 27/32] fix: bind native launch and packaging to local runtime --- cli/launch.go | 10 +++++++++ packaging/build-flint-runtime.py | 35 ++++++++++++++++++++++++++----- packaging/package.py | 36 ++++++++++++++++++++++++++++++++ tests/package_test.py | 7 +++++-- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/cli/launch.go b/cli/launch.go index 57b6e59..7081f01 100644 --- a/cli/launch.go +++ b/cli/launch.go @@ -167,6 +167,13 @@ func openingClient(name string, s Status) { newScreen(os.Stderr).step(accentStyle.Render("→"), "Opening "+name, fmt.Sprintf("Flint at %s, %s tokens", s.Endpoint, formatTokens(s.Context))) } +func validateLaunchAutostart(m Manifest) error { + if m.Engine == "flint-native" { + return fmt.Errorf("native Flint launch requires an existing local server; start it with rec serve flint --model-dir DIR") + } + return nil +} + func openCodeArgs(args []string) []string { // V2's TUI selects its model from config; --model is a run-only flag. // A private OpenCode server receives this process's Rec config and API key, @@ -217,6 +224,9 @@ func launchReady(ctx context.Context, p Paths, out io.Writer) (Status, error) { if e != nil { return s, e } + if e = validateLaunchAutostart(m); e != nil { + return s, e + } if !modelFilesPresent(p, m) { if e = preflight(m); e != nil { return s, e diff --git a/packaging/build-flint-runtime.py b/packaging/build-flint-runtime.py index c4a30b1..175aadb 100644 --- a/packaging/build-flint-runtime.py +++ b/packaging/build-flint-runtime.py @@ -4,8 +4,8 @@ import argparse import hashlib import json +import os import re -import shutil import stat import subprocess from pathlib import Path @@ -21,13 +21,25 @@ def main(): info = source.lstat() if source.is_symlink() or not stat.S_ISREG(info.st_mode) or not info.st_mode & 0o111: parser.error("--flint-serve must be an executable regular file, not a symlink") + fd = os.open(source, os.O_RDONLY | os.O_CLOEXEC | os.O_NOFOLLOW) + opened = os.fstat(fd) + identity = lambda value: ( + value.st_dev, value.st_ino, value.st_mode, value.st_size, + value.st_mtime_ns, value.st_ctime_ns, + ) + if (info.st_dev, info.st_ino) != (opened.st_dev, opened.st_ino): + os.close(fd) + parser.error("--flint-serve changed while being opened") + executable = f"/proc/self/fd/{fd}" probe = subprocess.run( - [str(source.resolve()), "--help"], capture_output=True, text=True, timeout=30 + [executable, "--help"], capture_output=True, text=True, timeout=30, + pass_fds=(fd,), ) if probe.returncode != 0 or "flint-serve" not in probe.stdout or "--checkpoint-dir" not in probe.stdout: parser.error("executable does not satisfy the native Flint server contract") contract_probe = subprocess.run( - [str(source.resolve()), "--runtime-contract"], capture_output=True, text=True, timeout=30 + [executable, "--runtime-contract"], capture_output=True, text=True, timeout=30, + pass_fds=(fd,), ) try: contract = json.loads(contract_probe.stdout) @@ -54,7 +66,11 @@ def main(): or contract.get("source_clean") is not True or not all(re.fullmatch(r"[0-9a-f]{40}", str(contract.get(key, ""))) for key in ("source_commit", "source_tree")) ): + os.close(fd) parser.error("executable runtime contract is missing, dirty, or incompatible") + if identity(os.fstat(fd)) != identity(opened): + os.close(fd) + parser.error("--flint-serve changed while being probed") requested_output = args.output if requested_output.exists() or requested_output.is_symlink(): @@ -62,9 +78,18 @@ def main(): output = requested_output.resolve() output.mkdir(parents=True, mode=0o700) destination = output / "flint-serve" - shutil.copyfile(source, destination) + digest = hashlib.sha256() + os.lseek(fd, 0, os.SEEK_SET) + with destination.open("xb") as staged: + while chunk := os.read(fd, 1024 * 1024): + staged.write(chunk) + digest.update(chunk) + if identity(os.fstat(fd)) != identity(opened): + os.close(fd) + parser.error("--flint-serve changed while being staged") + os.close(fd) destination.chmod(0o755) - binary_sha256 = hashlib.sha256(destination.read_bytes()).hexdigest() + binary_sha256 = digest.hexdigest() (output / "build.json").write_text( json.dumps( {**{key: fixed[key] for key in ("engine", "os", "arch", "backend")}, diff --git a/packaging/package.py b/packaging/package.py index 6433b7c..1791bdc 100644 --- a/packaging/package.py +++ b/packaging/package.py @@ -19,12 +19,48 @@ def sha(data): return hashlib.sha256(data).hexdigest() +def validate_native_runtime(runtime_dir, manifest, metadata): + if manifest["engine"] != "flint-native": + return + server = runtime_dir / "flint-serve" + if server.is_symlink() or not server.is_file(): + raise ValueError("native runtime contract executable is missing or unsafe") + expected_keys = {"engine", "os", "arch", "backend", "binary_sha256", "runtime_contract"} + if set(metadata) != expected_keys: + raise ValueError("native runtime contract metadata is incomplete") + if metadata["binary_sha256"] != sha(server.read_bytes()): + raise ValueError("native runtime contract binary digest mismatch") + contract = metadata["runtime_contract"] + fixed = { + "schema": 1, "engine": "flint-native", "os": "linux", "arch": "amd64", + "backend": "hip", "revision": manifest["revision"], + "max_context": manifest["max_context"], "arena_base_bytes": 25074426720, + "kv_bytes_per_token": 28672, "sampler_bytes": 1951872, + } + source_keys = {"source_commit", "source_tree", "source_clean"} + if (not isinstance(contract, dict) or set(contract) != set(fixed) | source_keys + or any(contract.get(key) != value for key, value in fixed.items()) + or contract.get("source_clean") is not True + or not all(re.fullmatch(r"[0-9a-f]{40}", str(contract.get(key, ""))) + for key in ("source_commit", "source_tree"))): + raise ValueError("native runtime contract is incompatible") + probe = subprocess.run([str(server.resolve()), "--runtime-contract"], + capture_output=True, text=True, timeout=30) + try: + live = json.loads(probe.stdout) + except json.JSONDecodeError: + live = None + if probe.returncode != 0 or live != contract: + raise ValueError("native runtime contract probe disagrees with metadata") + + def payload(runtime_dir, manifest): m = json.loads(manifest.read_text()) metadata = json.loads((runtime_dir / "build.json").read_text()) for key in ("engine", "os", "arch", "backend"): if metadata[key] != m[key]: raise ValueError(f"runtime and model disagree on {key}") + validate_native_runtime(runtime_dir, m, metadata) files = [] buf = io.BytesIO() # Fixed gzip timestamp makes equivalent payloads content-identical. diff --git a/tests/package_test.py b/tests/package_test.py index 78faffd..7aa7683 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -124,8 +124,11 @@ check=True, ) staged = bound_runtime / "flint-serve" - assert b"MUTATED-NO-CONTRACT" not in staged.read_bytes(), staged.read_bytes() - assert subprocess.run([str(staged), "--runtime-contract"], capture_output=True).returncode == 0 + staged_probe = subprocess.run( + [str(staged), "--runtime-contract"], capture_output=True, text=True + ) + assert staged_probe.returncode == 0 + assert json.loads(staged_probe.stdout) == identity["runtime_contract"], staged_probe.stdout # Native packages must not trust a four-field build.json around an arbitrary executable. hip_manifest = root / "packaging/flint-int4-hip.json" From 41ab162e8a15fc57b18263f6f1463848b557f5c2 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:51:26 +0800 Subject: [PATCH 28/32] test: reject self-attested native runtime forgeries --- tests/package_test.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/package_test.py b/tests/package_test.py index 7aa7683..22c1e86 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -135,17 +135,24 @@ arbitrary = work / "arbitrary-runtime" arbitrary.mkdir() arbitrary_server = arbitrary / "flint-serve" - arbitrary_server.write_text("#!/bin/sh\nexit 0\n") + arbitrary_contract = dict(identity["runtime_contract"]) + arbitrary_server.write_text( + "#!/bin/sh\n" + f"if [ \"$1\" = --runtime-contract ]; then echo '{json.dumps(arbitrary_contract, separators=(',', ':'))}'; exit 0; fi\n" + "exit 0\n" + ) arbitrary_server.chmod(0o755) arbitrary_m = json.loads(hip_manifest.read_text()) - (arbitrary / "build.json").write_text( - json.dumps({k: arbitrary_m[k] for k in ("engine", "os", "arch", "backend")}) - ) + (arbitrary / "build.json").write_text(json.dumps({ + **{k: arbitrary_m[k] for k in ("engine", "os", "arch", "backend")}, + "binary_sha256": hashlib.sha256(arbitrary_server.read_bytes()).hexdigest(), + "runtime_contract": arbitrary_contract, + })) rejected = subprocess.run( [sys.executable, str(root / "packaging/package.py"), "--runtime-dir", str(arbitrary), "--manifest", str(hip_manifest), "--version", "0.1.0-test", "--output", str(work / "bad-native-rec")], capture_output=True, text=True, ) - assert rejected.returncode != 0 and "runtime contract" in rejected.stderr, rejected.stderr + assert rejected.returncode != 0 and "trusted runtime" in rejected.stderr, rejected.stderr print("Packaging, archive validation and runtime/manifest mismatch rejection passed") From 3310dd1b89d0f4f53c29e2fa0c06c471a33f380b Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:53:26 +0800 Subject: [PATCH 29/32] fix: pin trusted native runtime provenance --- cli/manifest.go | 38 +++++++++++++++++++++-------------- cli/rec_test.go | 9 ++++++--- packaging/flint-int4-hip.json | 3 +++ packaging/package.py | 14 +++++++++++++ 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/cli/manifest.go b/cli/manifest.go index 488bb8c..dee8c94 100644 --- a/cli/manifest.go +++ b/cli/manifest.go @@ -31,21 +31,24 @@ type File struct { } type Manifest struct { - Engine string `json:"engine"` - Schema int `json:"schema"` - Model string `json:"model"` - Revision string `json:"revision"` - Development bool `json:"development"` - OS string `json:"os"` - Arch string `json:"arch"` - Backend string `json:"backend"` - Context int `json:"context"` - MaxContext int `json:"max_context,omitempty"` - MinRAM int64 `json:"min_ram_bytes"` - Files []File `json:"files"` - Entry string `json:"entry"` - RuntimeSHA string `json:"runtime_sha256"` - RuntimeFiles []File `json:"runtime_files"` + Engine string `json:"engine"` + Schema int `json:"schema"` + Model string `json:"model"` + Revision string `json:"revision"` + Development bool `json:"development"` + OS string `json:"os"` + Arch string `json:"arch"` + Backend string `json:"backend"` + Context int `json:"context"` + MaxContext int `json:"max_context,omitempty"` + MinRAM int64 `json:"min_ram_bytes"` + Files []File `json:"files"` + Entry string `json:"entry"` + RuntimeSHA string `json:"runtime_sha256"` + RuntimeFiles []File `json:"runtime_files"` + RuntimeBinarySHA string `json:"runtime_binary_sha256,omitempty"` + RuntimeSourceCommit string `json:"runtime_source_commit,omitempty"` + RuntimeSourceTree string `json:"runtime_source_tree,omitempty"` } func validHash(s string) bool { @@ -105,6 +108,11 @@ func decodeManifest(data []byte) (Manifest, error) { (m.Revision != nativeFlintRevision || m.MaxContext != nativeFlintMaxContext || m.Context > nativeFlintMaxContext) { return m, fmt.Errorf("native Flint revision or context does not match the runtime contract") } + if m.Engine == "flint-native" && + (!validHash(m.RuntimeBinarySHA) || !regexp.MustCompile(`^[0-9a-f]{40}$`).MatchString(m.RuntimeSourceCommit) || + !regexp.MustCompile(`^[0-9a-f]{40}$`).MatchString(m.RuntimeSourceTree)) { + return m, fmt.Errorf("native Flint trusted runtime pins are missing") + } if m.Schema != 1 || !safeName(m.Model) || m.Revision == "" || m.Context < 256 || m.MinRAM <= 0 || len(m.Files) == 0 { return m, fmt.Errorf("incomplete model descriptor") } diff --git a/cli/rec_test.go b/cli/rec_test.go index 0a475c5..d4f9d1f 100644 --- a/cli/rec_test.go +++ b/cli/rec_test.go @@ -362,9 +362,12 @@ func TestManifestAcceptsPinnedNativeHIPTarget(t *testing.T) { Revision: "3b93f9053b530488405ae9fca51d4900d6ce21f0", OS: "linux", Arch: "amd64", Backend: "hip", Context: 262144, MaxContext: 262144, MinRAM: 1, Entry: "config.json", - Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, - RuntimeSHA: digest([]byte("runtime")), - RuntimeFiles: []File{{Name: "flint-serve", Size: 1, SHA256: digest([]byte("r")), Executable: true}}, + Files: []File{{Name: "config.json", Size: 1, SHA256: digest([]byte("x")), URL: "https://example.com/config.json"}}, + RuntimeSHA: digest([]byte("runtime")), + RuntimeFiles: []File{{Name: "flint-serve", Size: 1, SHA256: digest([]byte("r")), Executable: true}}, + RuntimeBinarySHA: digest([]byte("r")), + RuntimeSourceCommit: strings.Repeat("a", 40), + RuntimeSourceTree: strings.Repeat("b", 40), } got, err := decodeManifest(mustJSON(t, m)) if err != nil { diff --git a/packaging/flint-int4-hip.json b/packaging/flint-int4-hip.json index cfebb3f..2087d0a 100644 --- a/packaging/flint-int4-hip.json +++ b/packaging/flint-int4-hip.json @@ -9,6 +9,9 @@ "backend": "hip", "context": 262144, "max_context": 262144, + "runtime_binary_sha256": "9217b560f9b71b65ce0587eb9d57aca41000b2afa527dc94dc984ee6d28c84d7", + "runtime_source_commit": "13965eff710a5de3b031b68b77604b232616502a", + "runtime_source_tree": "de744eb50a9f5c0e15faaccdedadb471ca420897", "min_ram_bytes": 8589934592, "entry": "flint-int4.json", "files": [ diff --git a/packaging/package.py b/packaging/package.py index 1791bdc..9296934 100644 --- a/packaging/package.py +++ b/packaging/package.py @@ -30,6 +30,17 @@ def validate_native_runtime(runtime_dir, manifest, metadata): raise ValueError("native runtime contract metadata is incomplete") if metadata["binary_sha256"] != sha(server.read_bytes()): raise ValueError("native runtime contract binary digest mismatch") + trusted = { + "binary": manifest.get("runtime_binary_sha256"), + "commit": manifest.get("runtime_source_commit"), + "tree": manifest.get("runtime_source_tree"), + } + if (not re.fullmatch(r"[0-9a-f]{64}", str(trusted["binary"] or "")) + or not all(re.fullmatch(r"[0-9a-f]{40}", str(trusted[key] or "")) + for key in ("commit", "tree"))): + raise ValueError("trusted runtime pins are missing") + if metadata["binary_sha256"] != trusted["binary"]: + raise ValueError("trusted runtime binary digest mismatch") contract = metadata["runtime_contract"] fixed = { "schema": 1, "engine": "flint-native", "os": "linux", "arch": "amd64", @@ -44,6 +55,9 @@ def validate_native_runtime(runtime_dir, manifest, metadata): or not all(re.fullmatch(r"[0-9a-f]{40}", str(contract.get(key, ""))) for key in ("source_commit", "source_tree"))): raise ValueError("native runtime contract is incompatible") + if (contract["source_commit"] != trusted["commit"] + or contract["source_tree"] != trusted["tree"]): + raise ValueError("trusted runtime source identity mismatch") probe = subprocess.run([str(server.resolve()), "--runtime-contract"], capture_output=True, text=True, timeout=30) try: From 37a8b1f9c00a535208686697451bd27dbe4ac237 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:56:28 +0800 Subject: [PATCH 30/32] test: reject caller-forged native trust manifests --- tests/package_test.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/package_test.py b/tests/package_test.py index 22c1e86..a8a5205 100644 --- a/tests/package_test.py +++ b/tests/package_test.py @@ -148,9 +148,16 @@ "binary_sha256": hashlib.sha256(arbitrary_server.read_bytes()).hexdigest(), "runtime_contract": arbitrary_contract, })) + forged_manifest = work / "forged-native-manifest.json" + arbitrary_m.update({ + "runtime_binary_sha256": hashlib.sha256(arbitrary_server.read_bytes()).hexdigest(), + "runtime_source_commit": arbitrary_contract["source_commit"], + "runtime_source_tree": arbitrary_contract["source_tree"], + }) + forged_manifest.write_text(json.dumps(arbitrary_m)) rejected = subprocess.run( [sys.executable, str(root / "packaging/package.py"), - "--runtime-dir", str(arbitrary), "--manifest", str(hip_manifest), + "--runtime-dir", str(arbitrary), "--manifest", str(forged_manifest), "--version", "0.1.0-test", "--output", str(work / "bad-native-rec")], capture_output=True, text=True, ) From b3d08667e5526c91a83c461af6feada8931a60b4 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Tue, 27 Oct 2026 15:57:14 +0800 Subject: [PATCH 31/32] fix: root native trust in reviewed package policy --- packaging/package.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packaging/package.py b/packaging/package.py index 9296934..555c570 100644 --- a/packaging/package.py +++ b/packaging/package.py @@ -14,6 +14,12 @@ import tempfile from pathlib import Path +TRUSTED_NATIVE_RUNTIME = { + "binary": "9217b560f9b71b65ce0587eb9d57aca41000b2afa527dc94dc984ee6d28c84d7", + "commit": "13965eff710a5de3b031b68b77604b232616502a", + "tree": "de744eb50a9f5c0e15faaccdedadb471ca420897", +} + def sha(data): return hashlib.sha256(data).hexdigest() @@ -39,6 +45,8 @@ def validate_native_runtime(runtime_dir, manifest, metadata): or not all(re.fullmatch(r"[0-9a-f]{40}", str(trusted[key] or "")) for key in ("commit", "tree"))): raise ValueError("trusted runtime pins are missing") + if trusted != TRUSTED_NATIVE_RUNTIME: + raise ValueError("trusted runtime manifest pins do not match the reviewed package policy") if metadata["binary_sha256"] != trusted["binary"]: raise ValueError("trusted runtime binary digest mismatch") contract = metadata["runtime_contract"] From b790dc83221132f7e2057b75754a9dc7d694ff34 Mon Sep 17 00:00:00 2001 From: AlexHe Date: Wed, 28 Oct 2026 08:12:20 +0800 Subject: [PATCH 32/32] chore: trust continuous batching Flint runtime --- packaging/flint-int4-hip.json | 6 +++--- packaging/package.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packaging/flint-int4-hip.json b/packaging/flint-int4-hip.json index 2087d0a..e780e1d 100644 --- a/packaging/flint-int4-hip.json +++ b/packaging/flint-int4-hip.json @@ -9,9 +9,9 @@ "backend": "hip", "context": 262144, "max_context": 262144, - "runtime_binary_sha256": "9217b560f9b71b65ce0587eb9d57aca41000b2afa527dc94dc984ee6d28c84d7", - "runtime_source_commit": "13965eff710a5de3b031b68b77604b232616502a", - "runtime_source_tree": "de744eb50a9f5c0e15faaccdedadb471ca420897", + "runtime_binary_sha256": "8fe875a4ccb9715c2d293dd3e0f6e389d0878a416de6679fa3daf7241c4c9b18", + "runtime_source_commit": "f09b8f04df3e1a44a2d03c3c7f5119dcc6793415", + "runtime_source_tree": "d7e75527a7c11cc71c14f68b2ebd72277e4fe592", "min_ram_bytes": 8589934592, "entry": "flint-int4.json", "files": [ diff --git a/packaging/package.py b/packaging/package.py index 555c570..6f0e9bb 100644 --- a/packaging/package.py +++ b/packaging/package.py @@ -15,9 +15,9 @@ from pathlib import Path TRUSTED_NATIVE_RUNTIME = { - "binary": "9217b560f9b71b65ce0587eb9d57aca41000b2afa527dc94dc984ee6d28c84d7", - "commit": "13965eff710a5de3b031b68b77604b232616502a", - "tree": "de744eb50a9f5c0e15faaccdedadb471ca420897", + "binary": "8fe875a4ccb9715c2d293dd3e0f6e389d0878a416de6679fa3daf7241c4c9b18", + "commit": "f09b8f04df3e1a44a2d03c3c7f5119dcc6793415", + "tree": "d7e75527a7c11cc71c14f68b2ebd72277e4fe592", }