From f257406d5c464bdce1a0c8d74128930d82107941 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 3 Nov 2026 21:56:39 +0000 Subject: [PATCH] feat(addons): expose the trusted identity of an installed add-on The Authority binds a grant to the identity of the installed package. It runs inside admission, which holds the manager lock, so it needs a read that does not take that lock. Keep the identities beside the completed operations and update them when an operation applies. Also export the check for the URL of the official library, which the blanket approval needs to tell the official library from a source an administrator named the same. Assisted-by: Claude:claude-sonnet-5-5 go --- core/services/addons/catalog/official.go | 4 +++ core/services/addons/grants.go | 33 ++++++++++++++++++- core/services/addons/manager.go | 6 ++++ core/services/addons/manager_identity_test.go | 33 +++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 core/services/addons/manager_identity_test.go diff --git a/core/services/addons/catalog/official.go b/core/services/addons/catalog/official.go index d6b5496744..aabb5c9bc0 100644 --- a/core/services/addons/catalog/official.go +++ b/core/services/addons/catalog/official.go @@ -73,6 +73,10 @@ func isOfficialAddonURL(url string) bool { return ok && asset == OfficialReleaseAsset && releaseVersionRE.MatchString(version) } +// IsOfficialAddonURL reports whether url is the index of the official add-on +// library that LocalAI itself ships: the master index or the index of a release. +func IsOfficialAddonURL(url string) bool { return isOfficialAddonURL(url) } + const ( // OfficialBackendLibraryID is the source ID of the backend library. OfficialBackendLibraryID = "official-backends" diff --git a/core/services/addons/grants.go b/core/services/addons/grants.go index d38e4a3364..135e2e21ab 100644 --- a/core/services/addons/grants.go +++ b/core/services/addons/grants.go @@ -121,7 +121,8 @@ func (m *Manager) markCompleted(operationID string) { m.completed[operationID] = true } -// rebuildCompleted reads the completed operations from the journal just loaded. +// rebuildCompleted reads the completed operations and the identities of the +// installed add-ons from the journal just loaded. func (m *Manager) rebuildCompleted() { m.grantsMu.Lock() defer m.grantsMu.Unlock() @@ -131,6 +132,36 @@ func (m *Manager) rebuildCompleted() { m.completed[id] = true } } + m.identities = map[string]string{} + for id, st := range m.journal.Addons { + if st.Installed { + m.identities[id] = st.Identity + } + } +} + +// TrustedIdentity returns the trusted identity of the installed package of an +// add-on, or empty when it is not installed. A grant of the owner is bound to +// it. Like OperationCompleted it never takes the manager lock, so an Authority +// may call it from inside a check that the manager makes under that lock. +func (m *Manager) TrustedIdentity(addonID string) string { + m.grantsMu.Lock() + defer m.grantsMu.Unlock() + return m.identities[addonID] +} + +// noteIdentity records the identity an applied operation left behind. +func (m *Manager) noteIdentity(addonID string, st Status) { + m.grantsMu.Lock() + defer m.grantsMu.Unlock() + if m.identities == nil { + m.identities = map[string]string{} + } + if st.Installed { + m.identities[addonID] = st.Identity + return + } + delete(m.identities, addonID) } // pendingAuthority adds the operations an install or upgrade in flight is diff --git a/core/services/addons/manager.go b/core/services/addons/manager.go index d55f74041a..e6c2304de5 100644 --- a/core/services/addons/manager.go +++ b/core/services/addons/manager.go @@ -172,6 +172,9 @@ type Manager struct { pending map[string][]string // completed holds the operations that completed; see OperationCompleted. completed map[string]bool + // identities maps an installed add-on to its trusted identity; see + // TrustedIdentity. + identities map[string]string } // SetAuthority sets the Authority that admission and the apps list ask. The @@ -816,6 +819,9 @@ func (m *Manager) apply(op journalOperation) (OperationStatus, error) { m.journal.Operations[op.Command.OperationID] = op e := m.persist(string(lifecycleCompleted)) if e == nil { + // The identity first: an entry whose operation completed must not meet + // an add-on the Authority does not know yet. + m.noteIdentity(op.Command.AddonID, m.journal.Addons[op.Command.AddonID]) m.markCompleted(op.Command.OperationID) } if e == nil && uninstall { diff --git a/core/services/addons/manager_identity_test.go b/core/services/addons/manager_identity_test.go new file mode 100644 index 0000000000..f6ad926605 --- /dev/null +++ b/core/services/addons/manager_identity_test.go @@ -0,0 +1,33 @@ +//go:build linux + +// SPDX-License-Identifier: MIT +package addons + +import ( + "github.com/mudler/LocalAI/core/services/addons/catalog" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("trusted identity of an installed add-on", func() { + It("follows install and uninstall without the manager lock", func() { + r := newUpdateRig(false) + Expect(r.m.TrustedIdentity("sample")).To(BeEmpty()) + r.lib.transform = nil + rel := r.lib.publish("1.2.3-dev", "development", "a", catalog.Requirements{}) + r.lib.refresh() + _, e := r.m.Submit(ctx, actor, LifecycleCommand{OperationID: "i1", AddonID: "sample", Action: "install", SourceID: "test", Version: "development"}) + Expect(e).NotTo(HaveOccurred(), rel.Digest) + Expect(r.m.TrustedIdentity("sample")).To(Equal("disk-one:sample")) + + // The Authority calls it from inside admission, which holds the lock. + r.m.mu.Lock() + got := r.m.TrustedIdentity("sample") + r.m.mu.Unlock() + Expect(got).To(Equal("disk-one:sample")) + + _, e = r.m.Submit(ctx, actor, LifecycleCommand{OperationID: "x1", AddonID: "sample", Action: "uninstall"}) + Expect(e).NotTo(HaveOccurred()) + Expect(r.m.TrustedIdentity("sample")).To(BeEmpty()) + }) +})