mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-23 22:54:56 -04:00
* feat(oci): pull ORAS artifacts into a directory Galleries published to an OCI registry arrive as an ORAS artifact whose layers carry their tree path in org.opencontainers.image.title. Nothing in pkg/oci could read a non-image manifest, so add PullArtifact: it lays the layers out under a destination directory and returns the manifest digest so callers can pin and verify signatures on it. A registry is remote input and this writes files, so the manifest is validated before any byte is fetched. A title that is absolute, escapes the destination or is missing is refused, an artifact of an unexpected artifactType is refused, and both the layer count and the total size are capped with caller-settable limits. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-5 [Claude Code] * feat(gallery): fetch and verify galleries published as OCI artifacts A gallery whose URL is oci://host/repo:tag is now pulled as an ORAS artifact and its index.yaml read from the unpacked tree, alongside the existing HTTP and file sources. Mirrors, the per-candidate timeout, the failure cooldown and the last known good copy work the same for both schemes, so a gallery can mirror an OCI primary with an HTTP fallback. When the gallery declares a verification policy the artifact reference is resolved to its manifest digest, the publisher signature is checked against that digest, and only then is the same digest pulled. Nothing unverified is written to disk or parsed. With strict integrity mode on, an OCI gallery without a verification policy is refused instead of warned about, which is what the flag already does for backend installs. The pull lands in a staging directory that is renamed into the cache only once the whole tree is on disk and the index reads back as an index, so a failed or interrupted pull leaves nothing a later fetch would serve. A fetch that finds a fresh cache entry does not contact the registry. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-5 [Claude Code] * feat(gallery): resolve entry urls relative to the gallery root A gallery published as a self-contained tree, which is what an OCI gallery is, names its base configs by their place in that tree. Such a url reached the HTTP client verbatim and no entry of the gallery could be installed. A scheme-less entry url now resolves against the root the index came from: the unpacked artifact for an oci:// gallery, and the directory of the index URL otherwise. The relative path may not climb out of that root, reusing the guard the artifact puller already applies to layer titles, and an entry that does is dropped from the listing rather than failing the whole gallery. An entry that names its own scheme is untouched, so .ref indirection and local github: resolution behave as before. Reading an entry of an OCI gallery also needs the unpacked artifact directory as its trusted root, since the cache is deliberately a sibling of the models directory the downloader otherwise confines a file read to. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-5 [Claude Code] * fix(oci): restrict artifact file permissions Create artifact files with owner-only permissions to satisfy G302. Document the existing path validation for the G304 scanner finding. Check the permissions of downloaded layers in the artifact test. Assisted-by: Codex:gpt-6 gosec * fix(oci): confine artifact writes to their root Use root-relative filesystem operations to reject escapes through existing directory symlinks. Retain owner-only permissions and add a symlink escape regression test, without suppressing the path traversal finding. Assisted-by: Codex:gpt-6 --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: localai-org-maint-bot <306269227+localai-org-maint-bot@users.noreply.github.com>
108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package system
|
|
|
|
import (
|
|
"github.com/mudler/LocalAI/pkg/xsysinfo"
|
|
"github.com/mudler/xlog"
|
|
)
|
|
|
|
type Backend struct {
|
|
BackendsPath string
|
|
BackendsSystemPath string
|
|
}
|
|
|
|
type Model struct {
|
|
ModelsPath string
|
|
}
|
|
|
|
type SystemState struct {
|
|
GPUVendor string
|
|
Backend Backend
|
|
Model Model
|
|
VRAM uint64
|
|
|
|
systemCapabilities string
|
|
|
|
// Backend image fallback tag configuration
|
|
BackendImagesReleaseTag string
|
|
BackendImagesBranchTag string
|
|
BackendDevSuffix string
|
|
// PreferDevelopmentBackends installs the development image as the primary
|
|
// backend URI (the released image becomes a fallback) rather than only using
|
|
// development as a download fallback when the released image is missing.
|
|
PreferDevelopmentBackends bool
|
|
|
|
// RequireBackendIntegrity is the strict-integrity switch bound to
|
|
// --require-backend-integrity / LOCALAI_REQUIRE_BACKEND_INTEGRITY. The
|
|
// backend install path takes it as a call argument; the gallery index
|
|
// fetch reads it from here because the listing code it runs under never
|
|
// sees the application config.
|
|
RequireBackendIntegrity bool
|
|
}
|
|
|
|
type SystemStateOptions func(*SystemState)
|
|
|
|
func WithBackendPath(path string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.Backend.BackendsPath = path
|
|
}
|
|
}
|
|
|
|
func WithBackendSystemPath(path string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.Backend.BackendsSystemPath = path
|
|
}
|
|
}
|
|
|
|
func WithModelPath(path string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.Model.ModelsPath = path
|
|
}
|
|
}
|
|
|
|
func WithBackendImagesReleaseTag(tag string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.BackendImagesReleaseTag = tag
|
|
}
|
|
}
|
|
|
|
func WithBackendImagesBranchTag(tag string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.BackendImagesBranchTag = tag
|
|
}
|
|
}
|
|
|
|
func WithBackendDevSuffix(suffix string) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.BackendDevSuffix = suffix
|
|
}
|
|
}
|
|
|
|
func WithRequireBackendIntegrity(require bool) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.RequireBackendIntegrity = require
|
|
}
|
|
}
|
|
|
|
func WithPreferDevelopmentBackends(prefer bool) SystemStateOptions {
|
|
return func(s *SystemState) {
|
|
s.PreferDevelopmentBackends = prefer
|
|
}
|
|
}
|
|
|
|
func GetSystemState(opts ...SystemStateOptions) (*SystemState, error) {
|
|
state := &SystemState{}
|
|
for _, opt := range opts {
|
|
opt(state)
|
|
}
|
|
|
|
// Detection is best-effort here, we don't want to fail if it fails
|
|
state.GPUVendor, _ = xsysinfo.DetectGPUVendor()
|
|
xlog.Debug("GPU vendor", "gpuVendor", state.GPUVendor)
|
|
state.VRAM, _ = xsysinfo.TotalAvailableVRAM()
|
|
xlog.Debug("Total available VRAM", "vram", state.VRAM)
|
|
|
|
state.getSystemCapabilities()
|
|
|
|
return state, nil
|
|
}
|