This is an automated email from the ASF dual-hosted git repository. hsluoyz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git
commit cdda2b7c0f2934d291b4837b9578305e3d985417 Author: Yang Luo <[email protected]> AuthorDate: Mon Aug 24 17:39:21 2026 +0800 feat(agent): monitor and record every agent by default --- agentpatch/auto.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ agentpatch/patch.go | 11 +++++-- conf/app.conf | 5 +-- conf/conf.go | 10 +++--- controllers/agent.go | 4 +++ main.go | 10 ++++++ 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/agentpatch/auto.go b/agentpatch/auto.go new file mode 100644 index 0000000..244f809 --- /dev/null +++ b/agentpatch/auto.go @@ -0,0 +1,86 @@ +// Copyright 2026 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agentpatch + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/apache/casbin-gateway/agent" +) + +// Monitoring is on by default: an installation Gateway finds is patched without +// waiting for a click. Turning one off by hand leaves an opt-out marker beside +// its manifest, so the next scan leaves that installation alone. + +func optOutPath(target Target) string { + return filepath.Join(stateDir(), targetKey(target)+".off") +} + +func isOptedOut(target Target) bool { + _, err := os.Stat(optOutPath(target)) + return err == nil +} + +func setOptOut(target Target, off bool) { + if !off { + _ = os.Remove(optOutPath(target)) + return + } + if err := os.MkdirAll(stateDir(), 0o700); err != nil { + return + } + _ = os.WriteFile(optOutPath(target), []byte(time.Now().Format(time.RFC3339)), 0o600) +} + +// EnsurePatched turns monitoring on for one installation, unless it cannot be +// monitored, already is, or was turned off by hand. +func EnsurePatched(target Target) error { + patcher, ok := patchers[target.AgentId] + if !ok || !patcher.Supported() || isOptedOut(target) { + return nil + } + + status, err := patcher.Status(target) + if err != nil { + return err + } + if status.Patched { + return nil + } + return patcher.Patch(target) +} + +// EnableAll turns monitoring on for every agent installed on this host. The +// failures are joined so one installation that cannot be patched neither hides +// the others nor stops them. +func EnableAll() error { + installations, err := agent.Scan(false) + if err != nil { + return err + } + + failures := []error{} + for _, installation := range installations { + target := Target{AgentId: installation.AgentId, Path: installation.Path, Owner: installation.Owner} + if err := EnsurePatched(target); err != nil { + failures = append(failures, fmt.Errorf("%s: %w", installation.AgentId, err)) + } + } + return errors.Join(failures...) +} diff --git a/agentpatch/patch.go b/agentpatch/patch.go index bd9f214..98fdb90 100644 --- a/agentpatch/patch.go +++ b/agentpatch/patch.go @@ -90,15 +90,22 @@ func Patch(target Target) error { if err != nil { return err } - return patcher.Patch(target) + if err := patcher.Patch(target); err != nil { + return err + } + setOptOut(target, false) + return nil } -// Unpatch disables monitoring and restores files changed by Patch. +// Unpatch disables monitoring and restores files changed by Patch. Monitoring +// being on by default, the choice is remembered so no later scan turns it back +// on. func Unpatch(target Target) error { patcher, err := patcherFor(target) if err != nil { return err } + setOptOut(target, true) return patcher.Unpatch(target) } diff --git a/conf/app.conf b/conf/app.conf index 7ceba26..9c580ee 100644 --- a/conf/app.conf +++ b/conf/app.conf @@ -104,8 +104,9 @@ agentMonitorPollSeconds = 5 ; LLM request records. "off" keeps nothing, "metadata" records who called which ; model with which outcome, and "full" also stores the sanitized request body. -; Anything beyond "off" is opt-in because prompts can carry sensitive data. -llmRecordMode = "off" +; The default is "full": the records are what the dashboards are made of, and +; they stay on this machine. Set it to "metadata" or "off" to keep less. +llmRecordMode = "full" llmRecordQueueCapacity = 1000 llmRecordRetentionDays = 30 llmRecordMaxRecords = 10000 diff --git a/conf/conf.go b/conf/conf.go index d68639a..c7defb4 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -300,16 +300,16 @@ const ( ) // GetLlmRecordMode reports whether LLM requests are recorded, and whether the -// request body is kept along with the metadata. Prompts are sensitive, so the -// default is to keep nothing. +// request body is kept along with the metadata. Recording is on by default, so +// anything but an explicit "off" or "metadata" keeps the whole request. func GetLlmRecordMode() string { switch strings.ToLower(strings.Trim(GetConfigString("llmRecordMode"), `"' `)) { + case LlmRecordOff: + return LlmRecordOff case LlmRecordMetadata: return LlmRecordMetadata - case LlmRecordFull: - return LlmRecordFull default: - return LlmRecordOff + return LlmRecordFull } } diff --git a/controllers/agent.go b/controllers/agent.go index 62bc600..fd8fc6a 100644 --- a/controllers/agent.go +++ b/controllers/agent.go @@ -68,6 +68,10 @@ func (c *ApiController) GetAgents() { result := make([]*discoveredAgent, 0, len(installations)) for _, installation := range installations { target := targetOf(installation) + // Monitoring is on by default, so an installation that is not monitored + // yet is patched here instead of waiting for the switch to be flipped. + // A failure is left to the status below, which says what is wrong. + _ = agentpatch.EnsurePatched(target) item := &discoveredAgent{ Installation: installation, Status: agentpatch.StatusOf(target), diff --git a/main.go b/main.go index 7a8d467..fe6c708 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "github.com/apache/casbin-gateway/agenthook" "github.com/apache/casbin-gateway/agentmonitor" + "github.com/apache/casbin-gateway/agentpatch" "github.com/apache/casbin-gateway/casdoor" "github.com/apache/casbin-gateway/conf" "github.com/apache/casbin-gateway/ip" @@ -93,6 +94,15 @@ func main() { } defer agentmonitor.Stop() + // Monitoring is on by default, so the agents already on this host are + // patched without anyone opening the UI. The scan walks the disk, hence the + // goroutine. + go func() { + if err := agentpatch.EnableAll(); err != nil { + beego.Error("agent monitoring could not be enabled:", err) + } + }() + // An update ends this process without unwinding main, so what the deferred // calls above would have flushed has to be flushed there instead. version.BeforeRestart = func() { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
