diff --git a/appl/cmd/webfs.b b/appl/cmd/webfs.b index 17bd3faea..d6e63518a 100644 --- a/appl/cmd/webfs.b +++ b/appl/cmd/webfs.b @@ -157,7 +157,7 @@ init(nil: ref Draw->Context, args: list of string) args = arg->argv(); arg = nil; - mountpt := "/n/web"; + mountpt := "/mnt/web"; if(args != nil) mountpt = hd args; diff --git a/appl/veltro/SECURITY.md b/appl/veltro/SECURITY.md index a8e3c8e30..cbdde4722 100644 --- a/appl/veltro/SECURITY.md +++ b/appl/veltro/SECURITY.md @@ -691,7 +691,7 @@ be explicit: namespace surface supports that distinction. The same rule applies to other fixed-function service trees. `/mnt/matrix` is -derived only from the `matrix` tool, `/n/git` only from the `git` tool, +derived only from the `matrix` tool, `/mnt/git` only from the `git` tool, `/mnt/gpu` only from `gpu` or local `vision`, `/n/wikia` only from `wiki`, `/mnt/video` only from video presentation tools, and `/phone` only from `sms`, `dial`, or `contacts`. diff --git a/appl/veltro/nsconstruct.b b/appl/veltro/nsconstruct.b index b619c861d..9f1dcc900 100644 --- a/appl/veltro/nsconstruct.b +++ b/appl/veltro/nsconstruct.b @@ -207,7 +207,6 @@ restrictns(caps: ref Capabilities): string # /n is the IMPORT YARD — foreign trees imported intact (docs/NAMESPACE-LAYOUT.md). # All /n/ entries are capability-driven — never auto-exposed by existence: # /n/speech — "/n/speech" in caps.paths - # /n/git — fixed git tool # /n/wallet — "/n/wallet" in caps.paths # /n/pres-* — caps.xenith != 0 # /n/local — /n/local/ subpaths in caps.paths @@ -231,15 +230,6 @@ restrictns(caps: ref Capabilities): string nallow = "speech" :: nallow; } - # /n/git — fixed-function git service. The git tool mounts git/fs here - # during trusted init; generic path grants cannot expose gitfs ctl/raw - # repository state to unrelated tools. - if(inlist("git", caps.tools)) { - (gitok, nil) := sys->stat("/n/git"); - if(gitok >= 0) - nallow = "git" :: nallow; - } - # /n/wallet — only if explicitly granted via caps.paths if(inlist("/n/wallet", caps.paths)) { (walletok, nil) := sys->stat("/n/wallet"); @@ -338,6 +328,15 @@ restrictns(caps: ref Capabilities): string if(gpuok >= 0 && !inlist("gpu", mntpaths)) mntpaths = "gpu" :: mntpaths; } + # /mnt/git — fixed-function git service (git/fs, mounted by the git tool + # during trusted init; migrated from /n/git per docs/NAMESPACE-LAYOUT.md, + # INFR-401). Derived only from the git tool; generic path grants cannot + # expose gitfs ctl/raw repository state to unrelated tools. + if(inlist("git", caps.tools)) { + (gitok, nil) := sys->stat("/mnt/git"); + if(gitok >= 0 && !inlist("git", mntpaths)) + mntpaths = "git" :: mntpaths; + } # /mnt/ui — presentation surface (luciuisrv), granted only to fixed-function # UI tools. Per-invocation caps prevent unrelated tools from inheriting it. # Capability-gated exactly as before, now under /mnt. The grant exposes the @@ -1103,6 +1102,7 @@ calendarcontrolpath(path: string): int fixedservicecontrolpath(path: string): int { return path == "/mnt/matrix" || prefix(path, "/mnt/matrix/") || + path == "/mnt/git" || prefix(path, "/mnt/git/") || path == "/n/git" || prefix(path, "/n/git/") || path == "/mnt/gpu" || prefix(path, "/mnt/gpu/") || path == "/mnt/web" || prefix(path, "/mnt/web/") || @@ -1258,7 +1258,6 @@ emitmanifest(caps: ref Capabilities, mpath: string) # /n entries — capability-driven (import yard) nentries := array[] of { ("/n/speech", "Speech", "rw"), - ("/n/git", "Git", "rw"), ("/n/wikia", "Wiki Agent", "rw"), ("/phone", "Phone Bridge", "rw"), # The LLM (llm9p), UI surface (luciuisrv) and MCP providers live under @@ -1269,6 +1268,7 @@ emitmanifest(caps: ref Capabilities, mpath: string) ("/mnt/mcp", "MCP Providers", "rw"), ("/mnt/matrix", "Matrix Runtime", "rw"), ("/mnt/gpu", "GPU Service", "rw"), + ("/mnt/git", "Git", "rw"), ("/mnt/web", "Web Service", "rw"), ("/mnt/wiki", "Wiki Store", "rw"), ("/mnt/registry", "Registry", "rw"), diff --git a/appl/veltro/tools/git.b b/appl/veltro/tools/git.b index e0ea0f4a4..0374beffa 100644 --- a/appl/veltro/tools/git.b +++ b/appl/veltro/tools/git.b @@ -3,7 +3,7 @@ implement ToolGit; # # git - Git repository access for Veltro agents # -# Read operations go through git/fs mounted at /n/git. +# Read operations go through git/fs mounted at /mnt/git. # Write operations go through a worker thread that retains # the unrestricted namespace (spawned before restriction). # @@ -69,7 +69,7 @@ init(): string if(ok < 0) return nil; # No repo; exec() will return errors - # Mount git/fs at /n/git before namespace restriction + # Mount git/fs at /mnt/git before namespace restriction ready := chan of int; spawn mountgitfs(ready); result := <-ready; @@ -96,7 +96,7 @@ mountgitfs(ready: chan of int) } { - gitfs->init(nil, "git/fs" :: "-m" :: "/n/git" :: "/.git" :: nil); + gitfs->init(nil, "git/fs" :: "-m" :: "/mnt/git" :: "/.git" :: nil); ready <-= 1; } exception { "*" => @@ -299,15 +299,15 @@ workercall(cmdline: string): string gitstatus(): string { - branch := strip(readfile("/n/git/ctl")); + branch := strip(readfile("/mnt/git/ctl")); if(branch == "") branch = "(unknown)"; - headhash := strip(readfile("/n/git/HEAD/hash")); + headhash := strip(readfile("/mnt/git/HEAD/hash")); if(headhash == "") return "On branch " + branch + "\n(no commits)"; - headmsg := strip(readfile("/n/git/HEAD/msg")); + headmsg := strip(readfile("/mnt/git/HEAD/msg")); (firstline, nil) := splitline(headmsg); return "On branch " + branch + "\n" + @@ -318,20 +318,20 @@ gitlog(n: int): string { result := ""; - hash := strip(readfile("/n/git/HEAD/hash")); + hash := strip(readfile("/mnt/git/HEAD/hash")); if(hash == "") return "(no commits)"; - author := strip(readfile("/n/git/HEAD/author")); - msg := strip(readfile("/n/git/HEAD/msg")); + author := strip(readfile("/mnt/git/HEAD/author")); + msg := strip(readfile("/mnt/git/HEAD/msg")); (firstline, nil) := splitline(msg); result = shorthash(hash) + " " + firstline + "\n"; result += " Author: " + author + "\n"; - parent := strip(readfile("/n/git/HEAD/parent")); + parent := strip(readfile("/mnt/git/HEAD/parent")); for(i := 1; i < n && parent != "" && parent != "nil"; i++) { - objdir := "/n/git/object/" + parent; + objdir := "/mnt/git/object/" + parent; author = strip(readfile(objdir + "/author")); msg = strip(readfile(objdir + "/msg")); @@ -351,16 +351,16 @@ gitshow(gitref: string): string objdir: string; if(len gitref == 40) { - objdir = "/n/git/object/" + gitref; + objdir = "/mnt/git/object/" + gitref; } else { - hash := strip(readfile("/n/git/branch/heads/" + gitref + "/hash")); + hash := strip(readfile("/mnt/git/branch/heads/" + gitref + "/hash")); if(hash != "") { - objdir = "/n/git/object/" + hash; + objdir = "/mnt/git/object/" + hash; gitref = hash; } else { - hash = strip(readfile("/n/git/tag/" + gitref + "/hash")); + hash = strip(readfile("/mnt/git/tag/" + gitref + "/hash")); if(hash != "") { - objdir = "/n/git/object/" + hash; + objdir = "/mnt/git/object/" + hash; gitref = hash; } else return "error: cannot find ref: " + gitref; @@ -413,9 +413,9 @@ gitshow(gitref: string): string gitbranch(): string { - current := strip(readfile("/n/git/ctl")); + current := strip(readfile("/mnt/git/ctl")); - entries := listdir("/n/git/branch/heads"); + entries := listdir("/mnt/git/branch/heads"); if(entries == nil) return "(no branches)"; @@ -428,10 +428,10 @@ gitbranch(): string result += " " + bname + "\n"; } - remotes := listdir("/n/git/branch/remotes"); + remotes := listdir("/mnt/git/branch/remotes"); for(; remotes != nil; remotes = tl remotes) { remote := hd remotes; - rbranches := listdir("/n/git/branch/remotes/" + remote); + rbranches := listdir("/mnt/git/branch/remotes/" + remote); for(; rbranches != nil; rbranches = tl rbranches) result += " remotes/" + remote + "/" + hd rbranches + "\n"; } @@ -441,7 +441,7 @@ gitbranch(): string gittag(): string { - entries := listdir("/n/git/tag"); + entries := listdir("/mnt/git/tag"); if(entries == nil) return "(no tags)"; @@ -462,16 +462,16 @@ gitcat(args: string): string treepath: string; if(gitref == "") { - treepath = "/n/git/HEAD/tree/" + fpath; + treepath = "/mnt/git/HEAD/tree/" + fpath; } else { - hash := strip(readfile("/n/git/branch/heads/" + gitref + "/hash")); + hash := strip(readfile("/mnt/git/branch/heads/" + gitref + "/hash")); if(hash == "") { if(len gitref == 40) hash = gitref; else return "error: cannot find ref: " + gitref; } - treepath = "/n/git/object/" + hash + "/tree/" + fpath; + treepath = "/mnt/git/object/" + hash + "/tree/" + fpath; } content := readfile(treepath); diff --git a/appl/veltro/tools9p.b b/appl/veltro/tools9p.b index f497b0ca1..36d8a2af7 100644 --- a/appl/veltro/tools9p.b +++ b/appl/veltro/tools9p.b @@ -933,6 +933,7 @@ calendarcontrolpath(path: string): int fixedservicecontrolpath(path: string): int { return path == "/mnt/matrix" || prefix(path, "/mnt/matrix/") || + path == "/mnt/git" || prefix(path, "/mnt/git/") || path == "/n/git" || prefix(path, "/n/git/") || path == "/mnt/gpu" || prefix(path, "/mnt/gpu/") || path == "/mnt/web" || prefix(path, "/mnt/web/") || diff --git a/dis/tests/veltro_security_test.dis b/dis/tests/veltro_security_test.dis index cd9f7793f..60cbbc2b7 100644 Binary files a/dis/tests/veltro_security_test.dis and b/dis/tests/veltro_security_test.dis differ diff --git a/dis/veltro/nsconstruct.dis b/dis/veltro/nsconstruct.dis index 940a98483..9d5700297 100644 Binary files a/dis/veltro/nsconstruct.dis and b/dis/veltro/nsconstruct.dis differ diff --git a/dis/veltro/tools/git.dis b/dis/veltro/tools/git.dis index 142af296d..4d58f6be2 100644 Binary files a/dis/veltro/tools/git.dis and b/dis/veltro/tools/git.dis differ diff --git a/dis/veltro/tools9p.dis b/dis/veltro/tools9p.dis index 78574f097..cd4fa228c 100644 Binary files a/dis/veltro/tools9p.dis and b/dis/veltro/tools9p.dis differ diff --git a/dis/webfs.dis b/dis/webfs.dis index 853dd8f0d..4ae6496b8 100644 Binary files a/dis/webfs.dis and b/dis/webfs.dis differ diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 3e6014954..1c3321873 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -154,7 +154,7 @@ Policy applied after `FORKNS`: - `/dis` → reduced to `lib/`, `veltro/` (+ `sh.dis` if `exec` tool active) - `/dis/veltro/tools/` → only registered tool `.dis` files visible - `/dev` → reduced to `cons`, `null`, `time` -- `/n` → capability-gated foreign imports: `/n/speech` only if in `caps.paths`; `/n/git` only for the fixed `git` tool +- `/n` → capability-gated foreign imports: `/n/speech` only if in `caps.paths` (`/mnt/git` is derived only for the fixed `git` tool — migrated from `/n/git`, INFR-401) - `/tmp` → writable only at `/tmp/veltro/scratch/` ### wallet9p (`appl/veltro/wallet9p.b`) diff --git a/docs/OPERATIONAL-OVERVIEW.md b/docs/OPERATIONAL-OVERVIEW.md index 031073ea8..2f8b75014 100644 --- a/docs/OPERATIONAL-OVERVIEW.md +++ b/docs/OPERATIONAL-OVERVIEW.md @@ -44,7 +44,7 @@ When an agent session starts, `nsconstruct` restricts the namespace: - `/dis` reduced to `lib/`, `veltro/` (+ `sh.dis` if exec is active) - `/dis/veltro/tools/` reduced to only the registered tool `.dis` files - `/dev` reduced to `cons`, `null`, `time` -- `/n` reduced to capability-gated foreign imports (`/n/speech` only if explicitly granted via paths; `/n/git` only for the fixed `git` tool) +- `/n` reduced to capability-gated foreign imports (`/n/speech` only if explicitly granted via paths); `/mnt/git` is derived only for the fixed `git` tool (migrated from `/n/git`, INFR-401) - `/tmp` writable only at `/tmp/veltro/scratch/` The agent cannot see files it wasn't granted. Subagents can only narrow further. diff --git a/docs/architecture-review-veltro-unification.md b/docs/architecture-review-veltro-unification.md index 82e6fea0e..473407b03 100644 --- a/docs/architecture-review-veltro-unification.md +++ b/docs/architecture-review-veltro-unification.md @@ -206,7 +206,7 @@ The pieces already exist: - **Phase 2**: A dedicated `compose` or `create` tool that handles the compile-register lifecycle, with appropriate sandboxing (the new tool's namespace is restricted by the creating agent's capabilities -- you can't escalate privileges by writing code). - **Phase 3**: The AI can introspect available modules, read their interfaces, and generate correct Limbo code that type-checks. The module system provides the contracts; the AI provides the composition. -The security model handles this naturally: a composed tool inherits the creating agent's namespace restrictions. You can't write a tool that accesses `/n/git` if your namespace doesn't include it. Capability attenuation is preserved even through code generation. +The security model handles this naturally: a composed tool inherits the creating agent's namespace restrictions. You can't write a tool that accesses `/mnt/git` if your namespace doesn't include it. Capability attenuation is preserved even through code generation. ## Design Note: The Semantic Shim as Temporary Adapter diff --git a/lib/veltro/meta.txt b/lib/veltro/meta.txt index 3e3279b66..d801d61cc 100644 --- a/lib/veltro/meta.txt +++ b/lib/veltro/meta.txt @@ -46,7 +46,7 @@ If you cannot fulfill a request — missing tools, unclear intent, or a failed d When creating a task, you construct its entire world: - tools= grants additional capabilities beyond the base set -- paths= grants filesystem visibility (/n/local/Users/pdfinn, /n/git, etc.) +- paths= grants filesystem visibility (/n/local/Users/pdfinn, /mnt/git, etc.) - instructions= provides procedural guidance for the task agent Put authority-bearing attrs (tools=, paths=, model=, agenttype=) before diff --git a/lib/veltro/nsaudit/authorities/git b/lib/veltro/nsaudit/authorities/git index 613cf8eed..b8d959b01 100644 --- a/lib/veltro/nsaudit/authorities/git +++ b/lib/veltro/nsaudit/authorities/git @@ -1,5 +1,5 @@ tool=git - description='Git repository access via git/fs at /n/git' + description='Git repository access via git/fs at /mnt/git' authorities='reads_fs writes_fs' irreversible='writes_fs' - notes='Read goes through /n/git. The WRITE path runs in a worker that retained the UNRESTRICTED namespace (spawned before restriction) — durable host mutation is reachable regardless of caps.paths. High-trust.' + notes='Read goes through /mnt/git. The WRITE path runs in a worker that retained the UNRESTRICTED namespace (spawned before restriction) — durable host mutation is reachable regardless of caps.paths. High-trust.' diff --git a/lib/veltro/tools/git.txt b/lib/veltro/tools/git.txt index b63bcd157..ea88b0497 100644 --- a/lib/veltro/tools/git.txt +++ b/lib/veltro/tools/git.txt @@ -1,7 +1,7 @@ git - Git repository access and management Provides full git repository access. Read operations use the native -git filesystem (git/fs at /n/git). Write operations use a worker +git filesystem (git/fs at /mnt/git). Write operations use a worker thread with direct repository access. Read commands: diff --git a/tests/nsaudit-rules/privileged-fixed-service-control-path/paths b/tests/nsaudit-rules/privileged-fixed-service-control-path/paths index 61171b960..9b7c588d2 100644 --- a/tests/nsaudit-rules/privileged-fixed-service-control-path/paths +++ b/tests/nsaudit-rules/privileged-fixed-service-control-path/paths @@ -20,3 +20,5 @@ /mnt/video /mnt/video/0/ctl /phone +/mnt/git +/mnt/git/ctl diff --git a/tests/veltro_security_test.b b/tests/veltro_security_test.b index 8917d8500..907686398 100644 --- a/tests/veltro_security_test.b +++ b/tests/veltro_security_test.b @@ -827,29 +827,30 @@ mntLlmWorker(result: chan of string) result <-= ""; } -# The git service is a fixed tool-derived /n import. The git tool must see -# /n/git without a raw path grant, while generic tools must not. +# The git service is a fixed tool-derived /mnt application mount (migrated +# from /n/git per docs/NAMESPACE-LAYOUT.md, INFR-401). The git tool must see +# /mnt/git without a raw path grant, while generic tools must not. testRestrictNsGitToolDerived(t: ref T) { - createdn := 0; - (ok, nil) := sys->stat("/n"); + createdmnt := 0; + (ok, nil) := sys->stat("/mnt"); if(ok < 0) { - fd := sys->create("/n", Sys->OREAD, Sys->DMDIR | 8r755); + fd := sys->create("/mnt", Sys->OREAD, Sys->DMDIR | 8r755); if(fd == nil) { - t.skip("cannot create /n test fixture"); + t.skip("cannot create /mnt test fixture"); return; } fd = nil; - createdn = 1; + createdmnt = 1; } createdgit := 0; - (ok, nil) = sys->stat("/n/git"); + (ok, nil) = sys->stat("/mnt/git"); if(ok < 0) { - fd := sys->create("/n/git", Sys->OREAD, Sys->DMDIR | 8r755); + fd := sys->create("/mnt/git", Sys->OREAD, Sys->DMDIR | 8r755); if(fd == nil) { - if(createdn) - sys->remove("/n"); - t.skip("cannot create /n/git test fixture"); + if(createdmnt) + sys->remove("/mnt"); + t.skip("cannot create /mnt/git test fixture"); return; } fd = nil; @@ -865,9 +866,9 @@ testRestrictNsGitToolDerived(t: ref T) } if(createdgit) - sys->remove("/n/git"); - if(createdn) - sys->remove("/n"); + sys->remove("/mnt/git"); + if(createdmnt) + sys->remove("/mnt"); if(r != "") t.error(r); } @@ -886,9 +887,9 @@ gitToolDerivedWorker(result: chan of string) result <-= sys->sprint("restrictns (git tool) failed: %s", err); return; } - (gitok, nil) := sys->stat("/n/git"); + (gitok, nil) := sys->stat("/mnt/git"); if(gitok < 0) { - result <-= "/n/git missing for git tool without raw path grant"; + result <-= "/mnt/git missing for git tool without raw path grant"; return; } result <-= ""; @@ -908,9 +909,9 @@ gitGenericHiddenWorker(result: chan of string) result <-= sys->sprint("restrictns (generic tool) failed: %s", err); return; } - (gitok, nil) := sys->stat("/n/git"); + (gitok, nil) := sys->stat("/mnt/git"); if(gitok >= 0) { - result <-= "/n/git visible to generic tool without git capability"; + result <-= "/mnt/git visible to generic tool without git capability"; return; } result <-= "";