odek's 21st-Century Security Posture
Depth-per-Dependency as a Defensive Architecture
odek's 21st-Century Security Posture
Depth-per-Dependency as a Defensive Architecture
odek is a minimal Go agent runtime that executes shell commands, reads and writes files, fetches URLs, and spawns sub-agents. That capability is the product — and it is also the attack surface. odek's answer is unusual among AI agents: rather than layering a permission system over a large dependency tree, it compresses the supply chain to five modules and spends the saved complexity budget on an adversarially-tested, layered defense stack.
This essay examines that architecture as implemented in the odek repository (August 2026 tree; internal audit register at b14771d [1]), then compares it to Claude Code and OpenAI Codex using each vendor's official security documentation [4][5][6][9][10][11].
The Thesis: Surface Area Is a Security Property
Most agent frameworks treat security as a bolt-on: a permission modal here, an allowlist there, wrapped around hundreds of transitive packages. odek inverts the problem.
Its dependency footprint is five modules — go-mcp, go-vector, and three golang.org/x/ packages (net, term, sys). go.sum contains ten entries across five unique modules [3]. The result is a ~11 MB static binary (release artifacts measure 10.6–11.6 MB across the four supported platforms [3]) with instant startup — and, more importantly, a supply chain small enough that one human can audit every line of third-party code the binary will ever load.
This is not aesthetic minimalism. Every dependency is a trusted execution domain: a package you cannot audit is a package that executes on your behalf with your credentials. Claude Code ships as a native binary behind thin installers (npm install -g @anthropic-ai/claude-code, Homebrew) [17] — but the binary itself is closed source, so its supply chain is taken on trust rather than verified. Codex CLI is open-source Rust — a strong memory-safety story — but its codex-rs/Cargo.lock resolves 1,359 locked crates [13]. odek's five-module closure is under 0.4% of that number, and every line of it is open to inspection.
The Threat Model — Explicit and Honest
odek's security documentation states, in writing, what it does and does not defend against [1]:
- In scope: prompt injection — an attacker plants instructions in content the agent ingests (a fetched page, a file outside the working directory, an MCP tool response, an audio transcript, a Telegram-forwarded message) — and approval fatigue, where the LLM produces a stream of approval prompts and the user reflex-clicks through one that turns out to be dangerous.
- Out of scope, stated plainly: a malicious user (the operator is assumed benign — Telegram mode requires an allowlist for exactly this reason), a malicious LLM provider (TLS is the only defense), and a model that ignores every defense ("the wrappers, classifications, and audit logs are only as strong as the model's training to honour them").
This candor is itself a security control. Most tools imply protection they cannot deliver; odek documents the boundary so operators deploy with accurate expectations. It is also explicitly a single-operator design.
The Layered Defense Stack
Seven layers stand between an injected payload and the operator's machine — from ingest, to execution, to what leaves the process:
flowchart LR
A["Injected payload
(page · file · MCP · transcript)"] --> B["Untrusted-content boundary
+ injection guard"]
B --> C["Danger classifier
normalize · decompose · fails closed"]
C --> D["Approval gate
friction-throttled"]
D --> E["Sandbox
no network · zero caps"]
E --> F["Redaction + audit
scrubbed output · hashed args"]
F --> G["Host"]Layer 0 — Minimal supply chain
Five modules, a static ~11 MB binary, nothing else. The foundation every other layer sits on.
Layer 1 — Sandboxed execution (`internal/sandbox`)
odek serve enables Docker sandboxing by default; odek run keeps it opt-in but warns loudly when unsandboxed. It is a genuine isolation boundary [2]:
- No network by default.
sandbox_networkdefaults tonone;hostis forcibly coerced back tononewith a warning. Exfiltration without an explicit operator choice is impossible. - Zero kernel capabilities even as root;
--security-opt no-new-privilegesblocks setuid/setgid;/tmpis anoexectmpfs. - File tools honor the sandbox.
write_file/patch/batch_patchtranslate host paths to/workspace/...and copy viadocker cp, so--sandbox-readonlyis enforceable against the agent's own tools — not just commands run throughshell. - Volume confinement. Extra bind mounts must resolve under the working directory, cannot contain
..or symlink escapes, and forbidden prefixes (/etc,/proc,/root,/var/run/docker.sock, …) are dropped. - Command-injection safety. The sandboxed command travels as a positional argument to the in-container wrapper — never string-interpolated — so shell quoting cannot break out of it.
- Repo-supplied
Dockerfile.odekbuilds are approval-gated, keyed on the file's content hash (editing it invalidates trust), and run with--network=nonesoRUNsteps cannot fetch payloads.
Layer 2 — The danger classifier (`internal/danger`, 3,211 LOC)
The heart of the posture. Shell commands are not regex-matched; they are parsed adversarially, because the threat model assumes a prompt-injected agent is actively trying to make a dangerous command read as harmless [1]:
- Normalization — the command is rewritten so token analysis can see through shell tricks before classification:
$'\x72\x6d'ANSI-C escapes,$IFSword-splitting (rm$IFS-rf$IFS/), brace expansion, command substitution,command/execwrappers, backslash escapes, and absolute paths. - Structural decomposition — commands are split on
;/&&/||, then into pipe stages, and every stage is classified — not just the head — sotrue | dd of=/dev/sdais caught. Pipelines intoxargshave their upstream literal payload composed onto the inner command. - Nine risk classes (
safe,local_write,system_write,destructive,network_egress,code_execution,install,blocked,unknown) mapped to allow/prompt/deny.
The defining property: the gate fails closed. An unrecognized verb classifies as unknown and is denied by default. A novel obfuscation that dodges every known-dangerous check cannot run.
Layer 3 — Approval gate with fatigue resistance
Dangerous commands trigger approval, but FrictionThreshold/FrictionWindow (3 prompts / 60s by default) throttle approval streams — treating approval fatigue as a first-class attack, not a UX annoyance [1].
Layer 4 — Prompt-injection guard (`internal/guard`)
A pluggable Guard interface with two backends: a zero-dependency local rule-based scan, and a piguard semantic sidecar (HTTP or Unix socket) as a second opinion [1]. The guard config is operator-only and rejected from project-level ./odek.json, so a malicious repo cannot disable the scan or redirect system-prompt content to an attacker endpoint.
Layer 5 — Secret redaction (`internal/redact`)
A lexical redaction layer sanitizes tool output before it reaches the transcript, session file, provider logs, or a Telegram chat [1]. It precomputes common encodings (base64, hex, URL, reversed) so echo $API_KEY | base64 does not leak; it matches known values registered from the environment plus format patterns; and it handles /proc/self/environ NUL-delimited dumps. The docs are equally honest about what redaction cannot do — arbitrary transformation and side-channel exfiltration are deferred to egress controls and approval gating.
Layer 6 — Untrusted-content boundary + SSRF
Every tool sourcing from outside the trust boundary wraps its result in a per-call nonce'd boundary tag, so an attacker cannot embed a literal close-tag to escape the wrapper — and the model always knows which content is untrusted. Network tools re-classify every redirect hop, and a dial guard blocks internal/metadata IPs (169.254.169.254), RFC 6598 CGNAT, and RFC 2544 ranges — closing SSRF against cloud metadata and overlay networks [1].
Layer 7 — Durability and hygiene
Atomic writes (O_CREATE|O_EXCL + fsync + rename), 0600/0700 state permissions, event-stream redaction (tool arguments logged only as SHA-256 digests), resource bounds everywhere (1 MiB shell output, 10 MiB reads), and SHA-256-verified upgrades [1].
The verification story
None of the above is aspirational — the defense stack is regression-tested. The six security-relevant packages (danger, guard, redact, sandbox, fsatomic, pathutil) carry 298 test functions, including dedicated classifier-bypass suites (classifier_bypass_test.go, audit_regressions_test.go, hardening_test.go) and a whitebox suite pinning normalization behaviour [2]. The repository documents the default suite as clean under go test -race ./..., with the Docker/subprocess E2E set behind an opt-in flag.
The Competitive Landscape — From Official Documentation
Claude Code *(Anthropic)*
- Permission-based architecture. Manual mode starts read-only; a built-in set of read-only commands (
ls,cat,git status) runs without asking. In auto mode, "a separate classifier model reviews actions instead of you and blocks the ones it judges unsafe" [4]. - Sandboxed Bash tool. OS-enforced filesystem and network isolation — macOS Seatbelt; Linux/WSL2 bubblewrap plus socat for the network proxy, with an optional seccomp filter that adds Unix-domain-socket blocking (
@anthropic-ai/sandbox-runtime). Native Windows is not supported (WSL2 required) [5]. - Documented network-proxy limit. The proxy "makes its allow decision from the client-supplied hostname without inspecting TLS", so allowing broad domains can enable domain-fronting exfiltration — a caveat Anthropic documents itself [5].
- Working-directory boundary. Manual mode can only write to the start folder and subfolders; reads outside the boundary prompt first [4].
- Prompt-injection defenses. Permission gating, context-aware analysis, input sanitization, network commands (
curl/wget) not auto-approved by default, isolated context windows for web fetch, and trust verification for first-time runs and new MCP servers [4]. - Approval-fatigue mitigation — allowlisting frequent safe commands per-user, per-codebase, or per-organization, plus an Accept Edits mode [4].
- Compliance posture. SOC 2 Type 2 and ISO 27001 via the Anthropic Trust Center [7], and a HackerOne bug-bounty program [8].
OpenAI Codex
- Two-layer model, explicitly named. "Sandbox mode: what Codex can do technically… Approval policy: when Codex must ask before it executes." The sandbox is a technical boundary; approvals are a decision boundary [9].
- Network off by default. "By default, the agent runs with network access turned off" [9].
- Platform-native sandboxing. macOS Seatbelt, native Windows sandbox (PowerShell), Linux/WSL2 bubblewrap [10].
- Sandbox and approval modes.
read-only,workspace-write(default),danger-full-access; Ask for approval (default), Approve for me (auto-review), Full access — with--ask-for-approval neverand--sandbox danger-full-accessas explicit, warned-against escape hatches [9][11]. - Network policy. A
network_proxyfeature with per-domain allow/deny rules, SOCKS5 support, and a Unix-socket allowlist; protected read-only paths (.git,.agents,.codex) [9]. - Rules engine. Starlark-based
.rulesfiles withprefix_rule()(prompt/allow/deny), admin-enforcedrequirements.toml, andcodex execpolicy checkfor offline testing [12]. - Audit and governance. OpenTelemetry events (
codex.tool_decision,codex.user_prompt— content redacted by default), a Codex security white paper [14], and a two-phase cloud runtime that strips configured secrets before the offline agent phase [9].
Comparison
| Axis | odek | Claude Code | Codex |
|---|---|---|---|
| Runtime / supply chain | Go, static 10.6–11.6 MB, 5 modules, open source | Native binary via npm/Homebrew; closed source | Rust, open source, 1,359 locked crates [13] |
| Sandbox mechanism | Docker container (default-on in serve) |
OS-native: Seatbelt / bubblewrap + socat proxy (optional seccomp) | OS-native: Seatbelt / bubblewrap / Windows sandbox |
| Network default | Off (none) |
Manual-mode approvals; sandbox isolates | Off |
| Command classification | Deterministic, token-based, fails closed (unknown → deny) |
Model-based classifier (auto mode) + read-only allowlist | Prefix rules (Starlark) + sandbox boundary |
| Obfuscation resistance | Normalization layers ($IFS, ANSI-C, xargs composition) |
Model judgment (not deterministic) | Prefix matching (not obfuscation-aware) |
| Approval-fatigue model | Explicit — friction throttling | Allowlisting per-scope | Sandbox reduces prompts; auto-review mode |
| Prompt-injection guard | Local scan + optional semantic sidecar | Context analysis + isolated fetch windows | Model-level + approval/sandbox |
| Secret redaction | First-class layer (encodings, known-values, proc-environ) | Not a documented layer | Not a documented layer (OTel user_prompt redacted) |
| SSRF hardening | Redirect re-classification + internal/metadata IP block | Partial (network approval) | Domain rules + Unix-socket allowlist |
| Threat model | Published, with named out-of-scope | Implicit | Implicit (white paper) |
| Adversarial test rigor | 298 test functions across 6 security packages, incl. bypass-regression suites [2] | Not published | Not published |
| Compliance / governance | — | SOC 2, ISO 27001, bug bounty | White paper, OTel audit, managed config |
A Convergent Architecture — and What Still Differentiates
The most significant finding from the official documentation is convergence: all three tools now ship the same two-layer mental model — a technical sandbox boundary (what the agent can do) plus an approval/classification layer (when it must stop and ask). Claude Code's "sandbox + permission modes" and Codex's "sandbox mode + approval policy" are, architecturally, the same idea. The difference is where the intelligence sits:
- Claude Code puts a second model in the loop to classify actions in auto mode [4].
- Codex puts a deterministic rules engine (Starlark
prefix_rule) plus OS sandboxing at the boundary [9][12]. - odek puts a deterministic, adversarially-hardened parser at the boundary — one that fails closed on the unknown and actively de-obfuscates shell tricks before judging [1].
Where odek leads
- Depth-per-dependency. Five modules versus Codex's 1,359 locked crates is a ~270× difference in third-party code; versus Claude Code, the differentiator is auditability — an open, enumerable closure against a closed binary.
- Fails-closed determinism. An unrecognized command is denied. Codex's prefix rules only match what you enumerate; Claude Code's auto-mode classifier is a model — fallible, not verifiable. odek's classifier is deterministic, testable, and ships bypass-regression suites.
- Adversarial shell normalization.
$IFS, ANSI-C escapes, andxargspayload composition are the specific evasions a prompt-injected agent will use — and odek is the only one of the three that documents a dedicated normalization layer for them (Claude Code'sBash(...)rules and Codex'sprefix_ruleare literal prefix matches). - Redaction as a first-class control. Neither competitor documents an equivalent tool-output secret-scrubbing layer.
- Published, honest threat model. odek's docs enumerate out-of-scope threats by name; neither competitor publishes an equivalent scope statement in its public docs.
Where odek trails
- Docker dependency. The strongest isolation layer needs Docker. Claude Code and Codex sandbox via OS primitives (Seatbelt/bubblewrap) with no daemon — strictly more portable.
- No model-level resistance. Claude Code and Codex benefit from frontier-lab instruction-honing against injection; odek's defense is entirely wrapper-level.
- No compliance/enterprise story. Claude Code has SOC 2/ISO 27001 and managed org policies; Codex has
requirements.toml, OTel audit events, and a security white paper. odek is a single-operator tool. - No second-opinion by default. The semantic piguard sidecar is opt-in; the local scan is rule-based only.
Honest Limitations
- Docker or nothing. Without Docker,
odek rundegrades to unsandboxed with a warning — a fallback the competitors do not need. - Lexical redaction is bounded. Arbitrary transformation and side-channel exfiltration defeat it by design [1].
- The model is the weak link. The entire stack is wrapper-level. The
sec_findings.mdregister documents a real critical finding (C-1: project./odek.jsonexfiltrating host secrets viasandbox_env${VAR}expansion) — now fixed — which is precisely the class of bug a small, auditable codebase lets you find and close quickly [1].
Conclusion
odek's posture is best summarized by its thesis: when the dependency tree is small enough to audit, the defense budget can be spent on the actual threat — an adversarial model wielding your shell. Its classifier fails closed, its sandbox is default-on and network-off, its redaction layer closes the tool-output exfiltration path, and its documentation is honest about what none of it can stop.
The comparison is not "odek is safer than Claude Code or Codex." It is that all three have converged on a sandbox-plus-approval architecture, and they differ on where the intelligence sits. Claude Code trusts a second model; Codex trusts a rules engine and OS sandbox; odek trusts a deterministic, de-obfuscating parser and a five-module supply chain. For a single operator who wants the smallest possible surface between an LLM and their machine — and the ability to read every line of code that runs with their credentials — that is a defensible, 21st-century answer.
Kyberneees, August 2026
References
[1] odek repository — https://github.com/BackendStack21/odek — docs/SECURITY.md, docs/SANDBOXING.md, docs/REDACTION_HARDENING.md, sec_findings.md.
[2] odek source — internal/danger/classifier.go, internal/danger/approver.go, internal/guard, internal/redact, internal/sandbox, internal/fsatomic.
[3] odek module graph and release artifacts — go.mod/go.sum (5 modules total: 4 direct + golang.org/x/sys; the entire transitive closure) and GitHub release binaries (10.6–11.6 MB across the four supported platforms).
[4] Anthropic, Security — https://code.claude.com/docs/en/security
[5] Anthropic, Configure the sandboxed Bash tool — https://code.claude.com/docs/en/sandboxing
[6] Anthropic, Configure permissions — https://code.claude.com/docs/en/permissions
[7] Anthropic Trust Center (SOC 2 Type 2, ISO 27001) — https://trust.anthropic.com
[8] Anthropic, Security Policy (HackerOne) — https://github.com/anthropics/claude-code/blob/main/SECURITY.md
[9] OpenAI, Agent approvals & security — https://learn.chatgpt.com/docs/agent-approvals-security
[10] OpenAI, Sandbox — https://learn.chatgpt.com/docs/sandboxing
[11] OpenAI, Permission modes — https://learn.chatgpt.com/docs/permission-modes
[12] OpenAI, Rules (exec policy) — https://learn.chatgpt.com/docs/agent-configuration/rules
[13] OpenAI, Codex CLI repository — https://github.com/openai/codex (codex-rs/Cargo.lock: 1,359 packages).
[14] OpenAI, Codex security white paper — https://trust.openai.com
[15] OWASP, Top 10 for LLM Applications — LLM01: Prompt Injection — https://genai.owasp.org
[16] NIST, Artificial Intelligence Risk Management Framework (AI RMF 1.0) — https://www.nist.gov/itl/ai-risk-management-framework
[17] Anthropic, Set up Claude Code (install methods) — https://code.claude.com/docs/en/setup