From 2ba9ba036c51475d7c216af49cf601748886277d Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Tue, 18 Aug 2026 12:49:25 +0200 Subject: [PATCH] Document native Landlock and persist session yolo --- README.md | 2 ++ cmd/olliesrv/internal/fs/spec.go | 5 ++++- cmd/olliesrv/internal/session/persist.go | 3 +++ cmd/olliesrv/internal/session/registry.go | 7 ++++--- cmd/olliesrv/internal/session/session.go | 5 +++-- doc/architecture-9p.md | 2 +- doc/architecture-toolsrv.md | 4 ++-- doc/usage.md | 4 ++++ 8 files changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4bf3ca4..20cd9a6 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ session/ ## Providers, tools, and security +`session/new` accepts `name`, `remote`, `workflow`, `cwd`, and `yolo=true` key/value fields. Session-level `yolo` is persisted and applies to local or remote toolsrv startup; it disables native Landlock enforcement for explicit development use. Normal tools use the configured native Landlock policy, while approved escape requests go through the bypass broker. + Provider backends are the model-facing boundary; the agent loop is independent of vendor APIs. Tools are external executable programs served by the separate `toolsrv` 9P process. Their metadata is discovered and loaded lazily, so the model sees only the capabilities needed for a task. Tool execution uses the configured native Landlock sandbox. Operations requiring an approved escape use the bypass namespace and broker, which applies policy and approval controls rather than providing an unrestricted escape. diff --git a/cmd/olliesrv/internal/fs/spec.go b/cmd/olliesrv/internal/fs/spec.go index 5f7999d..ededd82 100644 --- a/cmd/olliesrv/internal/fs/spec.go +++ b/cmd/olliesrv/internal/fs/spec.go @@ -257,6 +257,7 @@ func buildTreeSpec(cfg *Config) virtfs.FsNodeDecl { virtfs.Rdwr(func(_ context.Context, data []byte) ([]byte, error) { args := strings.Fields(string(data)) name, remote, workflow, cwd := "", "", "", "" + yolo := false for _, arg := range args { if k, v, ok := strings.Cut(arg, "="); ok { switch k { @@ -268,13 +269,15 @@ func buildTreeSpec(cfg *Config) virtfs.FsNodeDecl { workflow = v case "cwd": cwd = v + case "yolo": + yolo = v == "true" } } } if cwd == "" { return nil, fmt.Errorf("cwd is required") } - sess, err := session.CreateEmpty(name, remote) + sess, err := session.CreateEmpty(name, remote, yolo) if err != nil { return nil, err } diff --git a/cmd/olliesrv/internal/session/persist.go b/cmd/olliesrv/internal/session/persist.go index 70f3ea7..3c69295 100644 --- a/cmd/olliesrv/internal/session/persist.go +++ b/cmd/olliesrv/internal/session/persist.go @@ -21,6 +21,7 @@ type PersistedSession struct { Name string `json:"name"` CWD string `json:"cwd"` Remote string `json:"remote,omitempty"` + Yolo bool `json:"yolo,omitempty"` Paused bool `json:"paused,omitempty"` Workflow string `json:"workflow,omitempty"` Goal string `json:"goal,omitempty"` @@ -70,6 +71,7 @@ func PersistSession(name string) error { Name: sess.Name(), CWD: sess.Cwd(), Remote: sess.Remote, + Yolo: sess.Yolo, Paused: sess.IsPaused(), Workflow: sess.Workflow(), Goal: goalText, @@ -190,6 +192,7 @@ func restoreMultiAgentSession(ps *PersistedSession) (*RestoredSession, error) { sess := NewEmpty(ps.ID, ctx, cancel) sess.SetName(ps.Name) sess.Remote = ps.Remote + sess.Yolo = ps.Yolo if ps.Paused { sess.mu.Lock() sess.paused = true diff --git a/cmd/olliesrv/internal/session/registry.go b/cmd/olliesrv/internal/session/registry.go index b0346cb..cf4eef9 100644 --- a/cmd/olliesrv/internal/session/registry.go +++ b/cmd/olliesrv/internal/session/registry.go @@ -284,7 +284,7 @@ func BuildAgentIndex(sess *Session) []byte { } // CreateEmpty creates an empty session (no agent) and registers it. -func CreateEmpty(name, remote string) (*Session, error) { +func CreateEmpty(name, remote string, yolo ...bool) (*Session, error) { sessID := util.NewUUID() if name == "" { name = sessID @@ -304,6 +304,7 @@ func CreateEmpty(name, remote string) (*Session, error) { sess := NewEmpty(sessID, ctx, cancel) sess.SetName(name) sess.Remote = remote + sess.Yolo = len(yolo) > 0 && yolo[0] cwd, _ := os.Getwd() infra, err := SetupToolServer(ToolServerConfig{ @@ -311,7 +312,7 @@ func CreateEmpty(name, remote string) (*Session, error) { CWD: cwd, RemoteTarget: remote, SessionID: sessID, - Yolo: pkgYolo, + Yolo: sess.Yolo, }) if err != nil { cancel() @@ -390,7 +391,7 @@ func buildAgent(sess *Session, p AgentParams) (*agent.Agent, error) { CWD: cwd, RemoteTarget: remote, SessionID: sessID, - Yolo: pkgYolo, + Yolo: sess.Yolo, ReuseFrom: reuseFrom, }) if err != nil { diff --git a/cmd/olliesrv/internal/session/session.go b/cmd/olliesrv/internal/session/session.go index 99bf6c4..851cddd 100644 --- a/cmd/olliesrv/internal/session/session.go +++ b/cmd/olliesrv/internal/session/session.go @@ -31,6 +31,7 @@ type Session struct { Proc *toolclient.Process Keeper *toolclient.ProcessKeeper Remote string + Yolo bool // Agent state agents []*agent.Agent @@ -433,7 +434,7 @@ func (s *Session) Resume() error { CWD: cwd, RemoteTarget: s.Remote, SessionID: s.ID, - Yolo: pkgYolo, + Yolo: s.Yolo, }) if err != nil { s.log.Error("Resume: toolsrv startup failed: %v", err) @@ -461,7 +462,7 @@ func (s *Session) Resume() error { CWD: cwd, RemoteTarget: s.Remote, SessionID: s.ID, - Yolo: pkgYolo, + Yolo: s.Yolo, }) if err != nil { cancel() diff --git a/doc/architecture-9p.md b/doc/architecture-9p.md index 7ef2513..57416ad 100644 --- a/doc/architecture-9p.md +++ b/doc/architecture-9p.md @@ -55,7 +55,7 @@ echo '{"prompt":"explain recursion"}' | ollie-9p rdwr generate | Path | Mode | Purpose | |---|---:|---| -| `session/new` | rdwr | Create a session. Write `name=X [remote=Y]`; read the resulting name. | +| `session/new` | rdwr | Create a session. Write `name=X [remote=Y] [yolo=true]`; read the resulting name. | | `session/idx` | r | Session index: `session-id\tsession-name\tpaused\tconnected\tremote\tcwd`. | | `session/{sname}/env` | r | Session environment and runtime variables. | | `session/{sname}/paused` | r | Pause state. | diff --git a/doc/architecture-toolsrv.md b/doc/architecture-toolsrv.md index cc80a60..f56151a 100644 --- a/doc/architecture-toolsrv.md +++ b/doc/architecture-toolsrv.md @@ -36,7 +36,7 @@ Startup sequence: 1. Require the `serve` subcommand and a `--listen` path. 2. Expand `~` in `--cwd`. 3. Create the log sink and call `util.EnsureEnv()`. -4. Start the native Landlock sandbox helper for each restricted tool execution. +4. Start a short-lived child helper from the toolsrv binary for each restricted tool execution. The helper applies the configured Landlock ruleset directly with Linux system calls, then `exec`s the tool command. 5. Create the per-agent registry and the server state. 6. Apply `--yolo` to the server and process state. 7. Build the 9P tree from `server.Spec`. @@ -220,7 +220,7 @@ advanced: add_exec: false ``` -Paths support `{CWD}`, `{HOME}`, `{TMPDIR}`, XDG variables, and Ollie configuration/data variables. The loaded policy is passed to the sandbox wrapper. `--yolo` skips enforcement for explicit development use. +Paths support `{CWD}`, `{HOME}`, `{TMPDIR}`, XDG variables, and Ollie configuration/data variables. The policy is encoded by the parent toolsrv process and passed to its own short-lived helper mode; no separately installed sandbox executable or wrapper is required. On Linux, the helper sets `no_new_privs`, creates a Landlock ruleset, adds the configured path rules, restricts itself, and executes the tool. `--yolo` skips enforcement for explicit development use. Sandbox validation rejects dangerous command patterns before execution. The execution service also rate-limits repeated validation failures. The bypass path is not a sandbox configuration override; it is a separately approved execution route. diff --git a/doc/usage.md b/doc/usage.md index 2a26b5c..5453370 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -29,6 +29,10 @@ Valid levels: `debug`, `info`, `warn`, `error`. --- +## Security and sandboxing + +`toolsrv` applies the configured policy directly through native Landlock in a short-lived child helper; it does not depend on an external sandbox executable. On Linux, the helper creates and restricts a Landlock ruleset before executing the tool. Use `yolo=true` only for explicit development cases where enforcement should be disabled. + ## `o` — Terminal CLI `o` is a multi-call shell script that wraps the 9P namespace into a human-friendly interface.