Repository navigation
Security
github-actions[bot] edited this page Nov 1, 2026
·
2 revisions
aikido runs model output, and model output is untrusted input: tool arguments can be malformed, can try path traversal, can name tools that do not exist, and a run can loop. This page lists what the library guarantees and what it leaves to you. One-line rules: Gotchas.
-
No operating system access by default. The library starts no processes (only
shell.Externalcommands you register do), opens no sockets other than the provider HTTP clients (llm/openrouter,llm/codex), reads no environment variables and never logs. The only package that touches the disk isvfs/osfs, and only under the directory you pass. The built-in tools have no shell, no network fetch, no code evaluation. The opt-intools/shellruns bash over avfs.Storage, not the host: its files are the store's, its commands Go code, host programs only those you wrap withshell.External(Shell). -
Tools see one store. The file tools work only on the
vfs.Storagegiven toRegisterVFSToolsand capture it by closure; no argument selects another store or tenant. Multi-tenant callers bind the tenant withScopedStorage.Scopebefore registration (ADR-013). -
Path validation. Every path argument passes
vfs.ValidatePath(no empty path, no leading or trailing slash, no//, no.or..segment, no backslash, no NUL, at most 512 bytes) and the caller policy (AllowedExtensions,HideHiddenPaths) before the storage is called; refused paths arevfs.ErrPathInvalid. The bundled backends validate again on entry. -
Directory confinement.
vfs/osfsresolves every path throughos.Root: neither..nor a symlink reaches outside the directory. Writes go to an owner-only temp file and are renamed over the target, so a reader never sees half a file and a replaced file keeps its permission bits (a0600file stays private). Only regular files are listed or read;.gitis skipped. -
Dry runs.
vfs/overlaynever writes its base beforeCommit;ChangesandUnifiedDiffshow what would happen,Discarddrops it.vfs/mountReadOnlymounts refuse writes whatever the storage supports. -
Bounded runs.
MaximumTurns(20),RunTimeout(10 min),LLMCallTimeout(180 s),ToolCallTimeout(2 min),MaximumToolResultBytes(64 KiB to the model),MaximumTokens(16384) and the tools' own caps:MaximumFileBytes(1 MiB written),read_file2000 lines or 40 KiB,grep200 entries and a 32 MiB scan,glob500 paths. The voice handler caps uploads at 8 MiB and turns at 5 minutes. - Containment. A failing, panicking or timed-out tool is a tool error the model sees; a panic anywhere else in a run, in a voice turn or in a provider's stream goroutine becomes an error event, never a crashed process. A cancelled caller cannot leave a run goroutine or a session lock behind.
-
Consistent History. A run that fails leaves History unchanged; runs of one session ID are serialized by the
Locker. -
Secrets. API keys come only from
Optionsand travel only as theAuthorizationheader to the configuredBaseURL.llm/codexwrites tokens nowhere: every rotated login goes to yourOnRefresh. Errors carry status, provider code and at most 256 bytes of the provider's message (APIError.Message), never the key.
-
Your tools are yours. A handler registered with
tools.Registrycan do anything Go can; review it like any code that runs model-chosen input.Dispatchchecks only the top-levelrequiredfields. -
ToolCallTimeoutbinds only handlers that honourctx. A handler that ignores its context blocks the run until it returns. -
No transactions. The loop is not transactional and
Commitis not atomic across files: it stops at the first failure with the earlier paths applied.move_filewrites, then deletes, and can leave both files. -
Whole-file reads.
vfs.Storagehas no ranged read, soread_fileloads the whole file even forlimit: 1, andgreploads every file it scans (up to 32 MiB per call) before applying its window. A store holding very large files costs that much memory perread_filecall; keep such files out of the store, or out of the agent's reach withAllowedExtensionsorHideHiddenPaths. An optional ranged-read capability is planned for a v1.x release. -
Moves lose the file mode.
vfs.Storagehas no rename and no mode, somove_filecreates a new destination with the store's default mode (vfs/osfs: 0644; an overwritten destination keeps its own), a case-only rename (Readme.mdtoREADME.md) included: a 0600 file comes out 0644. The same holds for an overlayCommitof such a rename. -
Overlapping mounts alias.
vfs/mountroutes by the path string aftervfs.ValidatePath; the storages resolve the files. A writable root over a directory and a read-only mount over its subdirectory are not separate: on a case-insensitive filesystem (macOS, Windows)Docs/a.mdreachesdocs/a.mdthrough the root, and so does a symlink in the root. Mount disjoint directories. - Prompt injection. Files the agent reads are model input; a document can instruct the model. The tools limit the reach to the store you gave it; review changes (overlay) before they reach anything that matters.
-
Single process locking.
agent.LocalLockerserializes within one process only. Several replicas need your ownLocker; without one, two replicas can interleave a transcript and race on files. -
No spending cap. A run can cost up to
MaximumTurnsmodel calls; there is no budget or rate limit per session or tenant. Put one in front if users trigger runs. -
Auth is yours.
voice.Handlerauthenticates nobody; check in a middleware and resolve the tenant inHandlerOptions.Session. - Output is not escaped. Model text goes to your users as is; escape it where you render it.
-
Content filters are detected heuristically. A provider that drops a stream without a signal looks like
ErrServerError. -
Codex logins are single-use refresh tokens: one
TokenSourceper login and process, andParseLoginshares the login with the Codex CLI.
Reporting: open a private security advisory on the GitHub repository.
Mirror of docs/wiki at v1.2.0 (dd94d84). Edit the repository, not this wiki.