Language As Security
If we think of an agentic workflows as a natural language program, tool call security is one of the most vexing yet interesting problems to solve. After quite some deliberation, my approach is to use the expression language itself as the security layer. While there are still some edge cases, with this approach much of the threat surface is substantially reduced and the bulk of the problem largely resolved.
The API security model
In traditional software, user applications interact with the underlying system kernel, its datasets and file system via API calls.
These API calls enforce compliance with access permissions to ensure that user software can only access and modify the files and data its authorized to.
Agentic tool call vulnerability
The problem with agentic tool calls stems from the context window containing the system prompt, user prompt and any external (untrusted) data — all mixed together inside the current inference request.
This poses all sorts of problems, including:
- Prompts can be poisoned by injection of malicious instructions.
- Model performance degrades when the context window is filled with additional tool specifications.
- Token entanglement with tool specifications can shift attention weights, degrading inference in subtle, unexpected and largely invisible ways.
Working with smaller open-weight self-hosted models brings these problems to the forefront. I find this is a positive thing. Rather than masking these issues with the bruit force scale of frontier models, their visibly elicits cleaver mitigation strategies. Nevertheless, the objective is the same, to have unsupervised agentic workflows run reliably to completion.
Security with language
Imogen creates its security layer using language. The presumption here is that an expression language with an intentionally limited vocabulary greatly reduces the threat surface to the system that governs it.
Starting with workflow design, the sequential structure defined the finite state machine and the implicit flow of operations which are encoded with Agentic Inference Markup (AIM). AIM is a strongly typed rule based declarative language where workflow state is governed by the composition of its data structure. As an analogy and a gross oversimplification, think of AIM like a spreadsheet file that contains data and formulas. This spreadsheet analogy was a deliberate UX decision intended to capitalize on the existing skills and mental models familiar to information workers’ who use spreadsheets in their day-to-day work. AIM is obviously more advanced than this, but the spreadsheet metaphor provides a useful starting point for understanding how AIM works.
Unlike spreadsheets, AIM orchestrates agentic patterns composed of reusable modifiers. It provides version control with journaling, allowing inference to be rolled back, debugged and replayed, sessions to be audited, and inference modifiers to be evaluated. AIM is also model agnostic. It supports multiple response formats tuned for simple and advanced models, allowing the prompt structure and response format to adapt to model capabilities with progressive fallback. AIM also validates data returned by inference, providing the convenience of sensible type promotion built on top of strongly typed rule definitions, analogous to spreadsheet cell formatting rules, or database schema field definitions.
An AIM workspace consists of a tree hierarchy of workflows, represented by files in a corresponding directory structure. Each workflow consists of steps defined by rules. Each rule consists of an identifier, a type definition, a constant value or an expression.
- parent.aim
- parent.jnl
- child.aim
- child.jnl
- sibling.aim
- sibling.jnl
- uncle.aim
- uncle.jnl
Inference data created by each workflow is stored sequentially, where changes are append to workflow files with both incremental partial saves and full saves marked by new version headers. This provides an audit history of changes made to each workflow. A separate journal file maps version offsets after each full save. This RESTful1 architecture provides deployable multi-user agentic workflow inference with Multi-Version Concurrency Control (MVCC) that is highly concurrent, robust and efficient.
Expressions are declared with the expression language AIM Expressions (AIMX), which provides a simple, concise, C-like declarative language for writing formulas to manage inference. AIMX syntax is unable to explicitly articulate workflow operation or control because it lacks these types of statements. This approach of using language as the security layer limits the scope of inference to a virtual sandbox. While its still possible for malicious prompt injection within variables to leak out, the pathways open to these potential vulnerabilities are well known ahead of time, so counter measures can be employed to contain them. Regardless, with AIMX inference context and extracted data operate independently from implicit workflow operation and control.
As a language, AIMX improves upon the familiar declarative syntax used in traditional spreadsheet formulas. It replaces the error-prone row and column A1 cell referencing system with C-like named identifiers. This opens the door to multi-part reference chaining using dot notation employee.salary, array indexing matrix[index].round(), method calling sales.average().round_to(2) and functional programming using anonymous closures plan.perform(task => $std.inference(task, context)). Striped of unnecessary elements (like explicit flow control statements), this simple subset of C-like syntax will be immediately recognized by readers familiar with Javascript, Typescript, C# etc. AIMX strikes a balance between simplicity and expressive power, making it effective for a wide range of tasks – from automating business processes to orchestrating complex agentic pipelines.
Grammar Operators (in order or precedence, highest to lowest)
| Operators | Description | Associativity |
|---|---|---|
( ) | parenthesis | left to right |
_ | empty | singular |
=> | closure | left to right |
. () [] | postfix | left to right |
! + - (cast_type) [task_status] | unary | right to left |
* / % | multiplicative | left to right |
+ - | additive | left to right |
< <= > >= | relational | left to right |
= != | equality | left to right |
& | and | left to right |
| | or | left to right |
? : | conditional | right to left |
, | array | left to right |
; | procedure | left to right (only inside closures) |
Unlike C, some of the operator syntax has been simplified.
| C Operator | Grammar | Reason |
|---|---|---|
== | = | AIMX accepts both, giving them the same meaning |
&& | & | AIMX accepts both, giving them the same meaning |
|| | | | AIMX accepts both, giving them the same meaning |
The expression grammar also has an special unary operator for defining tasks.
[x] "A task that is completed"
[-] "A task that has failed"
[ ] "An array of pending tasks", [] "Next task", [] "Final task"As a result of this task syntax, the AIMX grammar uses C like parenthesis to define arrays, unlike JSON which uses [box] notation.
("an", "array", "of", "text", "literals")
foo("a", "function", "argument", "list")Security by design
In my opinion, the current crop of vibe coding applications which give models access to shell commands running within the permissive environment necessary for software development create large porous threat surfaces that are extremely vulnerable to malicious attacks. With access to the shell and commands like curl, almost any potential threat is conceivable. Emerging threat vectors from malicious IDE extensions and library dependency exploitations are continuing to raise concerns. It not acceptable to simultaneously warn users not to give agents unsupervised access to the shell while simultaneously making this modality essential for autonomous operation.
Security is a hard problem to solve. Imogen was designed from the ground up to prevent common inference vulnerabilities, not only to the underlying operating system. Imogen is written with the memory-safe programming language Rust. Imogen is also designed to protect the user, their data and their work-products from the potential threats agentic inference exposes. With Imogen, every inference step is contained inside a security layer provided by the AIMX language itself. This is security by design, not as an inconvenience, or an afterthought.
Dissertation Fielding, Roy Thomas (2000) Architectural Styles and the Design of Network-based Software Architectures. ↩︎