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
124 changes: 124 additions & 0 deletions internal/bootstrap/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package bootstrap

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)

const (
AuditArtifactLlamaServer = "llama_server"
AuditArtifactModel = "model"
)

type InstalledManifest struct {
ManifestURL string `json:"manifest_url"`
ManifestVersion int `json:"manifest_version"`
Platform string `json:"platform"`
InstalledAt string `json:"installed_at"` // UTC RFC3339 timestamp
ModelVariant string `json:"model_variant,omitempty"`
Artifacts map[string]InstalledArtifact `json:"artifacts"`
}

type InstalledArtifact struct {
// Tarball info
URL string `json:"url"`
SHA256 string `json:"sha256"`

// Extracted binary (e.g., llama-server)
Path string `json:"path"` // SHA of this file may not matched with SHA of URL
Size int64 `json:"size,omitempty"`
}

func InstalledManifestPath(p *Paths) string {
return filepath.Join(p.Home, "installed.json")
}

func WriteInstalledManifest(p *Paths, im *InstalledManifest) error {
data, err := json.MarshalIndent(im, "", " ")
if err != nil {
return fmt.Errorf("installed.json: marshal: %w", err)
}

target := InstalledManifestPath(p)
if err := os.MkdirAll(filepath.Dir(target), 0o700); err != nil {
return fmt.Errorf("installed.json: mkdir parent: %w", err)
}

// Atomic write by writing to tmp then renaming
tmp, err := os.CreateTemp(filepath.Dir(target), ".installed.json.*")
if err != nil {
return fmt.Errorf("installed.json: tmp: %w", err)
}

tmpName := tmp.Name()
defer os.Remove(tmpName)

if _, err := tmp.Write(data); err != nil {
_ = tmp.Close()
return fmt.Errorf("installed.json: write: %w", err)
}
if err := tmp.Close(); err != nil {
return fmt.Errorf("installed.json: close: %w", err)
}
if err := os.Chmod(tmpName, 0o600); err != nil {
return fmt.Errorf("installed.json: chmod: %w", err)
}
if err := os.Rename(tmpName, target); err != nil {
return fmt.Errorf("installed.json: rename: %w", err)
}

return nil
}

func ReadInstalledManifest(p *Paths) (*InstalledManifest, error) {
data, err := os.ReadFile(InstalledManifestPath(p))
if err != nil {
return nil, err
}

var record InstalledManifest
if err := json.Unmarshal(data, &record); err != nil {
return nil, fmt.Errorf("installed.json: parse: %w", err)
}

return &record, nil
}

func recordInstall(p *Paths, manifestURL string, manifestVersion int, modelVariant string, artifacts map[string]InstalledArtifact) error {
cur, err := ReadInstalledManifest(p)
if err != nil && !os.IsNotExist(err) {
cur = nil // use fresh data on failure
}
if cur == nil {
cur = &InstalledManifest{}
}

if cur.Artifacts == nil {
cur.Artifacts = map[string]InstalledArtifact{}
}

cur.ManifestURL = manifestURL
cur.ManifestVersion = manifestVersion
cur.Platform = PlatformTuple()
cur.InstalledAt = time.Now().UTC().Format(time.RFC3339)
if modelVariant != "" {
cur.ModelVariant = modelVariant
}
for k, v := range artifacts {
cur.Artifacts[k] = v
}

return WriteInstalledManifest(p, cur)
}

func statSize(path string, fallback int64) int64 {
info, err := os.Stat(path)
if err != nil {
return fallback
}

return info.Size()
}
212 changes: 212 additions & 0 deletions internal/bootstrap/audit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package bootstrap

import (
"encoding/json"
"os"
"testing"
)

func newAuditTestPaths(t *testing.T) *Paths {
t.Helper()
t.Setenv(EnvHome, t.TempDir())

paths, err := Resolve()
if err != nil {
t.Fatalf("Resolve: %v", err)
}
if err := paths.EnsureDirs(); err != nil {
t.Fatalf("EnsureDirs: %v", err)
}

return paths
}

func TestWriteAndReadInstalledManifest_RoundTrip(t *testing.T) {
paths := newAuditTestPaths(t)

manifest := &InstalledManifest{
ManifestURL: "https://example/manifest.json",
ManifestVersion: 1,
Platform: PlatformTuple(),
InstalledAt: "2026-05-31T00:00:00Z",
ModelVariant: "qwen3-embedding-0.6b.q6_K",
Artifacts: map[string]InstalledArtifact{
AuditArtifactLlamaServer: {URL: "https://example/llama.tar.gz", SHA256: "aaa", Path: "/x/bin/llama-server", Size: 1234},
AuditArtifactModel: {URL: "https://example/qwen3.gguf", SHA256: "bbb", Path: "/x/models/v.gguf", Size: 5678},
},
}
if err := WriteInstalledManifest(paths, manifest); err != nil {
t.Fatalf("WriteInstalledManifest: %v", err)
}

info, err := os.Stat(InstalledManifestPath(paths))
if err != nil {
t.Fatalf("stat: %v", err)
}
if perm := info.Mode().Perm(); perm != 0o600 {
t.Errorf("mode = %v, want 0o600", perm)
}

got, err := ReadInstalledManifest(paths)
if err != nil {
t.Fatalf("ReadInstalledManifest: %v", err)
}
if got.ManifestURL != manifest.ManifestURL ||
got.ManifestVersion != manifest.ManifestVersion ||
got.ModelVariant != manifest.ModelVariant {
t.Errorf("top-level mismatch: got %+v want %+v", got, manifest)
}
if got.Artifacts[AuditArtifactLlamaServer].SHA256 != "aaa" {
t.Errorf("llama_server sha = %q", got.Artifacts[AuditArtifactLlamaServer].SHA256)
}
if got.Artifacts[AuditArtifactModel].URL != "https://example/qwen3.gguf" {
t.Errorf("model url = %q", got.Artifacts[AuditArtifactModel].URL)
}
}

func TestReadInstalledManifest_NotInstalled(t *testing.T) {
paths := newAuditTestPaths(t)

_, err := ReadInstalledManifest(paths)
if !os.IsNotExist(err) {
t.Errorf("err = %v, want os.IsNotExist (no install has run)", err)
}
}

func TestRecordInstall_OverwritesAtomically(t *testing.T) {
paths := newAuditTestPaths(t)

// Initial installation
if err := recordInstall(paths, "https://m", 1, "", map[string]InstalledArtifact{
AuditArtifactLlamaServer: {URL: "first", SHA256: "1"},
}); err != nil {
t.Fatalf("recordInstall #1: %v", err)
}

// Update (or re-install)
if err := recordInstall(paths, "https://m", 1, "", map[string]InstalledArtifact{
AuditArtifactLlamaServer: {URL: "second", SHA256: "2"},
}); err != nil {
t.Fatalf("recordInstall #2: %v", err)
}

got, err := ReadInstalledManifest(paths)
if err != nil {
t.Fatalf("ReadInstalledManifest: %v", err)
}
if got.Artifacts[AuditArtifactLlamaServer].URL != "second" {
t.Errorf("URL = %q, want second", got.Artifacts[AuditArtifactLlamaServer].URL)
}
}

// recordInstall must preserve a previously-written artifact when a partial
// install only knows about one slot. EnsureLlamaServer followed by
// EnsureModel must not wipe the llama_server entry.
func TestRecordInstall_PreservesExistingArtifacts(t *testing.T) {
paths := newAuditTestPaths(t)

// First install: llama_server only
if err := recordInstall(paths, "https://m1", 1, "", map[string]InstalledArtifact{
AuditArtifactLlamaServer: {URL: "u-llama", SHA256: "s-llama", Path: "/p/llama"},
}); err != nil {
t.Fatalf("recordInstall #1: %v", err)
}

// Second install: model only
if err := recordInstall(paths, "https://m2", 1, "qwen", map[string]InstalledArtifact{
AuditArtifactModel: {URL: "u-model", SHA256: "s-model", Path: "/p/model"},
}); err != nil {
t.Fatalf("recordInstall #2: %v", err)
}

got, err := ReadInstalledManifest(paths)
if err != nil {
t.Fatalf("ReadInstalledManifest: %v", err)
}

if got.ManifestURL != "https://m2" {
t.Errorf("manifest_url = %q, want last write", got.ManifestURL)
}
if got.ModelVariant != "qwen" {
t.Errorf("model_variant = %q", got.ModelVariant)
}

// recordInstall must preserve previously written artifact
if a, ok := got.Artifacts[AuditArtifactLlamaServer]; !ok || a.URL != "u-llama" {
t.Errorf("llama_server entry lost or wrong: %+v", a)
}
if a, ok := got.Artifacts[AuditArtifactModel]; !ok || a.URL != "u-model" {
t.Errorf("model entry missing: %+v", a)
}
}

func TestRecordInstall_RecoversFromCorruptFile(t *testing.T) {
paths := newAuditTestPaths(t)

if err := os.WriteFile(InstalledManifestPath(paths), []byte("{not json"), 0o600); err != nil {
t.Fatalf("seed corrupt file: %v", err)
}

if err := recordInstall(paths, "https://m", 1, "v", map[string]InstalledArtifact{
AuditArtifactModel: {URL: "u", SHA256: "s", Path: "/p"},
}); err != nil {
t.Fatalf("recordInstall: %v", err)
}

got, err := ReadInstalledManifest(paths)
if err != nil {
t.Fatalf("ReadInstalledManifest: %v", err)
}
if got.Artifacts[AuditArtifactModel].URL != "u" {
t.Errorf("post-recovery URL = %q", got.Artifacts[AuditArtifactModel].URL)
}
}

func TestInstalledManifest_JSONShape(t *testing.T) {
paths := newAuditTestPaths(t)

manifest := &InstalledManifest{
ManifestURL: "https://m",
ManifestVersion: 1,
Platform: "linux-amd64",
InstalledAt: "2026-01-01T00:00:00Z",
ModelVariant: "v",
Artifacts: map[string]InstalledArtifact{
AuditArtifactLlamaServer: {URL: "u", SHA256: "s", Path: "/p", Size: 9},
},
}
if err := WriteInstalledManifest(paths, manifest); err != nil {
t.Fatalf("Write: %v", err)
}

data, err := os.ReadFile(InstalledManifestPath(paths))
if err != nil {
t.Fatal(err)
}

var raw map[string]any
if err := json.Unmarshal(data, &raw); err != nil {
t.Fatal(err)
}

for _, k := range []string{"manifest_url", "manifest_version", "platform", "installed_at", "model_variant", "artifacts"} {
if _, ok := raw[k]; !ok {
t.Errorf("missing top-level key %q", k)
}
}

arts, ok := raw["artifacts"].(map[string]any)
if !ok {
t.Fatal("artifacts not an object")
}
llama, ok := arts["llama_server"].(map[string]any)
if !ok {
t.Fatal("artifacts.llama_server missing")
}

for _, k := range []string{"url", "sha256", "path", "size"} {
if _, ok := llama[k]; !ok {
t.Errorf("artifacts.llama_server.%s missing", k)
}
}
}
Loading
Loading