Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Security Restrictions

Security Restrictions (SR) are themed capability groups a host can disable for a Final Session. When a theme bit is set in the Session’s disabled-set mask, tagged system words and type members that require that capability fail with a fixed "operation not allowed" — at compile time, interpret time, dictionary lookup, and as a safety net on native / compound invoke.

Policy is enforced in Go at language boundaries, not inferred from prompts. That makes SRs suitable for giving an AI (or any untrusted script source) access to Final without opening the filesystem, the network, or threading.

File factories, HTTP dialing, and pip spawn already participate in this model; see also File Streams, Web Dialing, and Threading.


Model

IdeaMeaning
Disabled-set maskInteger bitfield on the Session / Task. Bits set = themes denied.
Tag on affordancePrivileged words and type members carry SR bits (e.g. file-open factories → FileRead / FileWrite).
Violation(taggedSR & Task.restrict) != 0 → deny
UnrestrictedMask 0 — full vocabulary (normal TUI / trusted host default)

Axioms such as arithmetic, stack ops, and str:: / dt:: families are not SR-gated. Denial starts at system colon words and Type members that are privileged affordances (open file, dial HTTP, spawn a pip, …).

On the Final side, theme constants live on the synthetic type SR:

SR.ModuleInstall
SR.ModuleImport
SR.FileRead
SR.FileWrite
SR.FileSystemRead
SR.HttpDial
SR.HttpListen
SR.Threads
SR.Db
SR.Crypto
SR.Mask

Active catalog

SRDisallows
ModuleInstallThe install axiom (and nested install from loadProject)
ModuleImportThe import axiom (and nested import from loadProject); pre-import in the host if scripts must still see a module
FileReadOpen-for-read / content stream factories from path strings
FileWriteOpen-for-write / create / append factories (includes directory creation)
FileSystemReadEnumerate / metadata / exists-style filesystem queries (not the same as opening content)
HttpDialOutbound HTTP client ops (HttpDialer.send, httpDial, …)
HttpListenCreating an HTTP listener
ThreadsCreating Pips / Pipes, spawn: / (spawn)
DbOpening a database via DbProvider.new
CryptoDigests (Hash.hash), Hex/Base64 encode/decode; later AEAD / sign / KDF / SecureRandom

Combine themes with bitwise OR. A typical AI tool Session denies file / network / threads (and often Db / Crypto) so the agent can define words and run math / data logic, but cannot touch disk, network, or crypto primitives. Leave Crypto enabled if the agent needs content-id digests.


Host usage

Session default

The disabled-set is fixed when the Session is created (copied onto the Task). Every normal Send runs under that mask.

  • Interactive CLI: final-go -restrict <mask> (hex-friendly), e.g. deny threads with -restrict 0x20000000000000
  • Embedders: sess.SetSecurityRestrictions(mask) after NewSession / NewLockSession, or sender.SetSecurityRestrictions(mask) on the handle from GetSender() / GetLockedSender() before Send. finalgo.NewREPLSession(module, restrict) still takes the mask at construction. NewSession() defaults to unrestricted (0). Prefer a restricted Session for any agent-facing tool.

Agent-shaped example

package main

import (
	"fmt"
	"log"

	"github.com/androticus/final-go/finalgo"
)

// Disabled-set: bits set = denied. Matches SR.FileRead | FileWrite | FileSystemRead |
// HttpDial | HttpListen | Threads (see catalog above).
const agentRestrict = int64(
	0x100000000000 | // FileRead
		0x200000000000 | // FileWrite
		0x400000000000 | // FileSystemRead
		0x2000000000000 | // HttpDial
		0x4000000000000 | // HttpListen
		0x20000000000000, // Threads
)

func main() {
	// Persistent stacks are fine for a long-lived tool Session; dictionary state
	// is what agents usually need across tool calls.
	sess, err := finalgo.NewREPLSession(finalgo.REPLModuleLocal, agentRestrict)
	if err != nil {
		log.Fatal(err)
	}
	defer sess.Close()

	snd := sess.GetSender()
	out, err := snd.Send(`3 4 + $`, 0)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(out) // 7 — pure compute still works

	// File / HTTP / spawn affordances under this mask fail with
	// "operation not allowed" instead of performing the side effect.
	_, err = snd.Send(`"secret.txt" TextReader.newFromFile?^`, 0)
	if err == nil {
		log.Fatal("expected restriction denial")
	}
	fmt.Println("denied:", err)
}

With that mask, an LLM tool backed by finalgo.NewFinalTool() can still run postfix math, build Lists/Maps, parse Json in memory, and compile helper words — without enabling file ops, web dialing, or threading.

For the shortest unrestricted agent path, finalgo.NewSession() remains fine in trusted environments; add a restrict mask whenever the script source is model- or user-controlled.

Per-Send extra restrictions

sender.SetSecurityRestrictions(mask) on the handle from GetSender() / GetLockedSender() ORs those bits with the Session floor for that Send. You can tighten a dispatch; you cannot lift a Session-level denial.


What SRs are not

  • Not a substitute for OS sandboxing, containerization, or network policy outside the process
  • Not instruction-budget or hard wall-clock kill (those are separate runtime options; timeoutS on Send is advisory)
  • Not path allowlists — host path roots, when added, complement SR bits rather than replace them

  • 2 Embedding Models — Session / Send and why agents prefer finalgo
  • Features — SRs listed in the survey
  • Normative design detail: docs/003-design-notes/143-security-restrictions.md