Files
Ilker ManapandClaude Opus 5.5 249a91aeec Guardrails turn off and on; a judge's refusal says which hazard; ctl guard judge ask
guard.enabled (unset is on) makes every policy check nothing while false,
live: llmgw ctl guard off/on set it in the cluster's settings, and ctl
status, ctl guard list and the guardrails page say when they are off.

A refusal by a judge names the hazard it saw - judge: S6,Specialized
Advice - and the verdict is kept with the request in a new usage column,
guard_rule, shown by ctl request, the request log and the guardrails page.

llmgw ctl guard judge ask has a judge read a text, past the cache, and shows
its raw answer and how it is read; the endpoint is kept out of the audit so
no text is recorded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:41:08 +02:00

120 lines
4.1 KiB
Go

package cli
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi"
"gitea.mynodes.xyz/ilker/llmgw/internal/settings"
)
func newGuardCmd(client func() *ctlClient, asJSON *bool) *cobra.Command {
c := &cobra.Command{
Use: "guard",
Short: "Guardrail policies: which apply where",
Long: "Policies are defined in the cluster's settings under guard.policies\n" +
"(llmgw ctl config edit; in force everywhere within seconds) — what requests and\n" +
"answers are checked for (personal data, secrets, prompt injection, patterns,\n" +
"tools) and what is done: flag, redact or block. guard.default applies to\n" +
"every request; attach others to an organization, team, project, user or\n" +
"key, and everything below is judged by them too. Policies narrow: one\n" +
"attached lower can tighten, never loosen, what applies above it.\n\n" +
"Targets are written org:Acme, team:Acme/ml, project:Acme/ml/training (or\n" +
"with IDs), key:KEY-ID or user:USER-ID. An attachment applies at once.",
}
c.AddCommand(newGuardJudgeCmd(client, asJSON))
for _, sw := range []struct {
use, short string
on bool
}{
{"on", "Turn the guardrails on: every policy checks again", true},
{"off", "Turn the guardrails off: no request is checked, whatever the policies say", false},
} {
var note string
cmd := &cobra.Command{
Use: sw.use,
Short: sw.short,
Long: "guard.enabled in the cluster's settings: in force on every control plane\n" +
"within seconds, without a restart. The policies and where they are attached\n" +
"stay as they are, and come back as they were when turned on again. While\n" +
"off, llmgw ctl status says so.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
if note == "" {
note = "guardrails " + sw.use
}
return changeSettings(cmd, client, note, func(doc *settings.Doc) error {
return doc.SetYAML([]string{"guard", "enabled"}, strconv.FormatBool(sw.on))
})
},
}
cmd.Flags().StringVar(&note, "note", "", "why, kept with the settings' version")
c.AddCommand(cmd)
}
c.AddCommand(&cobra.Command{
Use: "list",
Short: "The policies, and where each is attached",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
var v adminapi.GuardView
if err := client().get(cmd.Context(), "/api/v1/guard", &v); err != nil {
return err
}
if *asJSON {
return json.NewEncoder(cmd.OutOrStdout()).Encode(v)
}
out := cmd.OutOrStdout()
if !v.Enabled {
fmt.Fprintln(out, "The guardrails are OFF: no request is checked (llmgw ctl guard on).")
}
fmt.Fprintf(out, "Policies: %s\n", strings.Join(v.Policies, ", "))
def := strings.Join(v.Default, ", ")
if def == "" {
def = "none"
}
fmt.Fprintf(out, "Applied to every request: %s\n", def)
if len(v.Attachments) == 0 {
fmt.Fprintln(out, "Attached nowhere else.")
return nil
}
fmt.Fprintln(out)
w := tab()
fmt.Fprintln(w, "SCOPE\tWHO\tPOLICY")
for _, a := range v.Attachments {
who := a.ScopeID
if a.Name != "" {
who = a.Name + " (" + a.ScopeID + ")"
}
fmt.Fprintf(w, "%s\t%s\t%s\n", a.Scope, who, a.Policy)
}
return w.Flush()
},
})
for _, verb := range []struct {
use, short, method, done string
}{
{"attach", "Apply a policy to a scope and everything below it", "POST", "attached to"},
{"detach", "Stop applying a policy to a scope", "DELETE", "detached from"},
} {
c.AddCommand(&cobra.Command{
Use: verb.use + " TARGET POLICY",
Short: verb.short,
Example: " llmgw ctl guard " + verb.use + " project:Acme/finance/payroll finance",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
body, _ := json.Marshal(map[string]string{"target": args[0], "policy": args[1]})
if err := client().do(cmd.Context(), verb.method, "/api/v1/guard/attachments", json.RawMessage(body), nil); err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "%s %s %s\n", args[1], verb.done, args[0])
return nil
},
})
}
return c
}