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
13 changes: 10 additions & 3 deletions core/application/startup.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func New(opts ...config.AppOption) (*Application, error) {
// Start background upgrade checker for backends.
// In distributed mode, uses PostgreSQL advisory lock so only one frontend
// instance runs periodic checks (avoids duplicate upgrades across replicas).
if len(options.BackendGalleries) > 0 {
if len(options.BackendGalleries) > 0 && !options.DisableBackendInstall {
// Pass a lazy getter for the backend manager so the checker always
// uses the active one — DistributedBackendManager is swapped in above
// and asks workers for their installed backends, which is what
Expand Down Expand Up @@ -536,14 +536,21 @@ func New(opts ...config.AppOption) (*Application, error) {
modelConfigResync.Start(options.Context, options.Distributed.ModelConfigResyncIntervalOrDefault(), distSvc.Nats)
}

// Preloads run outside the operation loop, so they do not get the
// availability the loop stamps on each op; it comes from the same manager.
preloadOptions := []gallery.InstallOption{gallery.WithArtifactMaterializer(options.ModelArtifactMaterializer)}
if gs := application.GalleryService(); gs != nil {
preloadOptions = append(preloadOptions, gallery.WithBackendAvailability(gs.BackendManager().BackendAvailable))
}

if options.PreloadJSONModels != "" {
if err := galleryop.ApplyGalleryFromString(options.SystemState, application.ModelLoader(), options.EnforcePredownloadScans, options.AutoloadBackendGalleries, options.Galleries, options.BackendGalleries, options.PreloadJSONModels, options.RequireBackendIntegrity, gallery.WithArtifactMaterializer(options.ModelArtifactMaterializer)); err != nil {
if err := galleryop.ApplyGalleryFromString(options.SystemState, application.ModelLoader(), options.EnforcePredownloadScans, options.AutoloadBackendGalleries, options.Galleries, options.BackendGalleries, options.PreloadJSONModels, options.RequireBackendIntegrity, preloadOptions...); err != nil {
return nil, err
}
}

if options.PreloadModelsFromPath != "" {
if err := galleryop.ApplyGalleryFromFile(options.SystemState, application.ModelLoader(), options.EnforcePredownloadScans, options.AutoloadBackendGalleries, options.Galleries, options.BackendGalleries, options.PreloadModelsFromPath, options.RequireBackendIntegrity, gallery.WithArtifactMaterializer(options.ModelArtifactMaterializer)); err != nil {
if err := galleryop.ApplyGalleryFromFile(options.SystemState, application.ModelLoader(), options.EnforcePredownloadScans, options.AutoloadBackendGalleries, options.Galleries, options.BackendGalleries, options.PreloadModelsFromPath, options.RequireBackendIntegrity, preloadOptions...); err != nil {
return nil, err
}
}
Expand Down
6 changes: 6 additions & 0 deletions core/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type RunCMD struct {
DisableRuntimeSettings bool `env:"LOCALAI_DISABLE_RUNTIME_SETTINGS,DISABLE_RUNTIME_SETTINGS" default:"false" help:"Disables the runtime settings. When set to true, the server will not load the runtime settings from the runtime_settings.json file" group:"api"`
DisablePredownloadScan bool `env:"LOCALAI_DISABLE_PREDOWNLOAD_SCAN" help:"If true, disables the best-effort security scanner before downloading any files." group:"hardening" default:"false"`
RequireBackendIntegrity bool `env:"LOCALAI_REQUIRE_BACKEND_INTEGRITY,REQUIRE_BACKEND_INTEGRITY" help:"If true, backend installs without a configured signature verification policy (for OCI URIs) or SHA256 (for tarball/HTTP URIs) are rejected. Default is to warn and install. Set this in production once your gallery's verification: block is populated." group:"hardening" default:"false"`
DisableBackendInstall bool `env:"LOCALAI_DISABLE_BACKEND_INSTALL,DISABLE_BACKEND_INSTALL" help:"If true, refuse to install or upgrade backends from galleries, OCI images, URLs or paths, including automatic installs, so the running backend set stays exactly what was deployed and tested; only backends already present or provided via --backends-system-path are used." group:"hardening" default:"false"`
OpaqueErrors bool `env:"LOCALAI_OPAQUE_ERRORS" default:"false" help:"If true, all error responses are replaced with blank 500 errors. This is intended only for hardening against information leaks and is normally not recommended." group:"hardening"`
UseSubtleKeyComparison bool `env:"LOCALAI_SUBTLE_KEY_COMPARISON" default:"false" help:"If true, API Key validation comparisons will be performed using constant-time comparisons rather than simple equality. This trades off performance on each request for resiliancy against timing attacks." group:"hardening"`
DisableApiKeyRequirementForHttpGet bool `env:"LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET" default:"false" help:"If true, a valid API key is not required to issue GET requests to portions of the web ui. This should only be enabled in secure testing environments" group:"hardening"`
Expand Down Expand Up @@ -302,6 +303,7 @@ func (r *RunCMD) Run(ctx *cliContext.Context) (result error) {
system.WithBackendDevSuffix(r.BackendDevSuffix),
system.WithPreferDevelopmentBackends(r.PreferDevelopmentBackends),
system.WithRequireBackendIntegrity(r.RequireBackendIntegrity),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down Expand Up @@ -785,6 +787,10 @@ func (r *RunCMD) Run(ctx *cliContext.Context) (result error) {
opts = append(opts, config.WithRequireBackendIntegrity(r.RequireBackendIntegrity))
}

if r.DisableBackendInstall {
opts = append(opts, config.WithDisableBackendInstall(r.DisableBackendInstall))
}

if r.PreferDevelopmentBackends {
opts = append(opts, config.WithPreferDevelopmentBackends(r.PreferDevelopmentBackends))
}
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type WorkerFlags struct {
BackendGalleries string `env:"LOCALAI_BACKEND_GALLERIES,BACKEND_GALLERIES" help:"JSON list of backend galleries" group:"backends" default:"${backends}"`
BackendsSystemPath string `env:"LOCALAI_BACKENDS_SYSTEM_PATH,BACKEND_SYSTEM_PATH" type:"path" default:"/var/lib/local-ai/backends" help:"Path containing system backends used for inferencing" group:"backends"`
RequireBackendIntegrity bool `env:"LOCALAI_REQUIRE_BACKEND_INTEGRITY,REQUIRE_BACKEND_INTEGRITY" help:"If true, reject backend installs without a configured signature verification policy (OCI URIs) or SHA256 (tarball/HTTP URIs)." group:"hardening" default:"false"`
DisableBackendInstall bool `env:"LOCALAI_DISABLE_BACKEND_INSTALL,DISABLE_BACKEND_INSTALL" help:"If true, never install the worker's backend from a gallery; use the one already present under the backends or system path, or exit with an error." group:"hardening" default:"false"`
ExtraLLamaCPPArgs string `name:"llama-cpp-args" env:"LOCALAI_EXTRA_LLAMA_CPP_ARGS,EXTRA_LLAMA_CPP_ARGS" help:"Extra arguments to pass to llama-cpp-rpc-server"`
}

Expand Down
47 changes: 47 additions & 0 deletions core/cli/worker/worker_backend_common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package worker

import (
"encoding/json"
"errors"
"os"
"path/filepath"

"github.com/mudler/LocalAI/core/gallery"
"github.com/mudler/LocalAI/pkg/system"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("findBackendPath with backend installation disabled", func() {
var state *system.SystemState

BeforeEach(func() {
root := GinkgoT().TempDir()
var err error
state, err = system.GetSystemState(
system.WithBackendPath(filepath.Join(root, "backends")),
system.WithBackendSystemPath(filepath.Join(root, "system")),
system.WithDisableBackendInstall(true),
)
Expect(err).NotTo(HaveOccurred())
Expect(os.MkdirAll(state.Backend.BackendsPath, 0o750)).To(Succeed())
Expect(os.MkdirAll(state.Backend.BackendsSystemPath, 0o750)).To(Succeed())
})

It("uses a backend that is already present", func() {
dir := filepath.Join(state.Backend.BackendsSystemPath, "vllm")
Expect(os.MkdirAll(dir, 0o750)).To(Succeed())
meta, _ := json.Marshal(&gallery.BackendMetadata{Name: "vllm"})
Expect(os.WriteFile(filepath.Join(dir, "metadata.json"), meta, 0o644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(dir, "run.sh"), []byte("#!/bin/sh\n"), 0o755)).To(Succeed())

path, err := findBackendPath("vllm", "[]", state, false)
Expect(err).NotTo(HaveOccurred())
Expect(path).To(Equal(dir))
})

It("refuses to install a missing backend", func() {
_, err := findBackendPath("vllm", "[]", state, false)
Expect(errors.Is(err, gallery.ErrBackendInstallDisabled)).To(BeTrue(), err.Error())
})
})
1 change: 1 addition & 0 deletions core/cli/worker/worker_ds4.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func (r *DS4Distributed) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker_llamacpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (r *LLamaCPP) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker_mlx_distributed.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func (r *MLXDistributed) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker_p2p.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func (r *P2P) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker_p2p_mlx.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func (r *P2PMLX) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/cli/worker/worker_vllm.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (r *VLLMDistributed) Run(ctx *cliContext.Context) error {
systemState, err := system.GetSystemState(
system.WithBackendPath(r.BackendsPath),
system.WithBackendSystemPath(r.BackendsSystemPath),
system.WithDisableBackendInstall(r.DisableBackendInstall),
)
if err != nil {
return fmt.Errorf("getting system state: %w", err)
Expand Down
8 changes: 8 additions & 0 deletions core/config/application_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ type ApplicationConfig struct {
// --require-backend-integrity / LOCALAI_REQUIRE_BACKEND_INTEGRITY.
RequireBackendIntegrity bool

// DisableBackendInstall mirrors SystemState.DisableBackendInstall for the
// HTTP layer: admission guards and the capability surfaces read it here.
DisableBackendInstall bool

SingleBackend bool // Deprecated: use MaxActiveBackends = 1 instead
MaxActiveBackends int // Maximum number of active backends (0 = unlimited, 1 = single backend mode)
WatchDogIdle bool
Expand Down Expand Up @@ -621,6 +625,10 @@ func WithRequireBackendIntegrity(v bool) AppOption {
return func(o *ApplicationConfig) { o.RequireBackendIntegrity = v }
}

func WithDisableBackendInstall(v bool) AppOption {
return func(o *ApplicationConfig) { o.DisableBackendInstall = v }
}

func WithPreferDevelopmentBackends(v bool) AppOption {
return func(o *ApplicationConfig) { o.PreferDevelopmentBackends = v }
}
Expand Down
Loading