Files
goclaw/internal/store/context.go
T
viettranx 843b550651 feat: runtime packages UI, pkg-helper, configurable shell deny groups (#244)
Runtime package management with security hardening:

- pkg-helper: root-privileged daemon for apk install/uninstall via Unix socket
- HTTP API: /v1/packages (list/install/uninstall/runtimes), admin role required for writes
- Shell deny groups: 15 configurable groups (per-agent overrides via context)
- Packages UI: Web page for managing system/pip/npm packages with confirmation dialogs
- Docker: privilege separation (root entrypoint → su-exec drop), init for zombie reaping
- Security: umask socket creation, persist file validation, deny pattern hardening
  (Node.js fetch/http, Python from/import, curl localhost, sensitive env vars)
- Auth: empty gateway token → admin role (dev/single-user mode)
2026-03-17 19:50:26 +07:00

140 lines
4.6 KiB
Go

package store
import (
"context"
"github.com/google/uuid"
)
type contextKey string
const (
// UserIDKey is the context key for the external user ID (TEXT, free-form).
UserIDKey contextKey = "goclaw_user_id"
// AgentIDKey is the context key for the agent UUID.
AgentIDKey contextKey = "goclaw_agent_id"
// AgentTypeKey is the context key for the agent type ("open" or "predefined").
AgentTypeKey contextKey = "goclaw_agent_type"
// SenderIDKey is the original individual sender's ID (not group-scoped).
// In group chats, UserIDKey is group-scoped but SenderIDKey preserves
// the actual person who sent the message.
SenderIDKey contextKey = "goclaw_sender_id"
// SelfEvolveKey indicates whether a predefined agent can update its SOUL.md.
SelfEvolveKey contextKey = "goclaw_self_evolve"
// LocaleKey is the context key for the user's preferred locale (e.g. "en", "vi", "zh").
LocaleKey contextKey = "goclaw_locale"
// SharedMemoryKey indicates memory should be shared (no per-user scoping).
SharedMemoryKey contextKey = "goclaw_shared_memory"
// ShellDenyGroupsKey holds per-agent shell deny group overrides.
ShellDenyGroupsKey contextKey = "goclaw_shell_deny_groups"
)
// WithShellDenyGroups returns a new context with shell deny group overrides.
func WithShellDenyGroups(ctx context.Context, groups map[string]bool) context.Context {
return context.WithValue(ctx, ShellDenyGroupsKey, groups)
}
// ShellDenyGroupsFromContext returns shell deny group overrides from the context, or nil.
func ShellDenyGroupsFromContext(ctx context.Context) map[string]bool {
v, _ := ctx.Value(ShellDenyGroupsKey).(map[string]bool)
return v
}
// WithUserID returns a new context with the given user ID.
func WithUserID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, UserIDKey, id)
}
// UserIDFromContext extracts the user ID from context. Returns "" if not set.
func UserIDFromContext(ctx context.Context) string {
if v, ok := ctx.Value(UserIDKey).(string); ok {
return v
}
return ""
}
// WithAgentID returns a new context with the given agent UUID.
func WithAgentID(ctx context.Context, id uuid.UUID) context.Context {
return context.WithValue(ctx, AgentIDKey, id)
}
// AgentIDFromContext extracts the agent UUID from context. Returns uuid.Nil if not set.
func AgentIDFromContext(ctx context.Context) uuid.UUID {
if v, ok := ctx.Value(AgentIDKey).(uuid.UUID); ok {
return v
}
return uuid.Nil
}
// WithAgentType returns a new context with the given agent type.
func WithAgentType(ctx context.Context, t string) context.Context {
return context.WithValue(ctx, AgentTypeKey, t)
}
// AgentTypeFromContext extracts the agent type from context. Returns "" if not set.
func AgentTypeFromContext(ctx context.Context) string {
if v, ok := ctx.Value(AgentTypeKey).(string); ok {
return v
}
return ""
}
// WithSenderID returns a new context with the original individual sender ID.
func WithSenderID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, SenderIDKey, id)
}
// SenderIDFromContext extracts the sender ID from context. Returns "" if not set.
func SenderIDFromContext(ctx context.Context) string {
if v, ok := ctx.Value(SenderIDKey).(string); ok {
return v
}
return ""
}
// WithSelfEvolve returns a new context with the self-evolve flag.
func WithSelfEvolve(ctx context.Context, v bool) context.Context {
return context.WithValue(ctx, SelfEvolveKey, v)
}
// SelfEvolveFromContext extracts the self-evolve flag from context. Returns false if not set.
func SelfEvolveFromContext(ctx context.Context) bool {
if v, ok := ctx.Value(SelfEvolveKey).(bool); ok {
return v
}
return false
}
// WithSharedMemory returns a context flagged for shared memory (skip per-user scoping).
func WithSharedMemory(ctx context.Context) context.Context {
return context.WithValue(ctx, SharedMemoryKey, true)
}
// IsSharedMemory returns true if memory should be shared across users.
func IsSharedMemory(ctx context.Context) bool {
v, _ := ctx.Value(SharedMemoryKey).(bool)
return v
}
// MemoryUserID returns the userID to use for memory operations.
// Returns "" (shared/global) when shared memory is active, otherwise the per-user ID.
func MemoryUserID(ctx context.Context) string {
if IsSharedMemory(ctx) {
return ""
}
return UserIDFromContext(ctx)
}
// WithLocale returns a new context with the given locale.
func WithLocale(ctx context.Context, locale string) context.Context {
return context.WithValue(ctx, LocaleKey, locale)
}
// LocaleFromContext extracts the locale from context. Returns "en" if not set.
func LocaleFromContext(ctx context.Context) string {
if v, ok := ctx.Value(LocaleKey).(string); ok && v != "" {
return v
}
return "en"
}