Prompt Injection
The concrete attack scenario and how each q15 layer defends against it.
When you talk to a raw LLM through an API, you send text and get text back. That is not an agent. An agent is the system around the model — the code that assembles the prompt, wires up tools, executes commands, manages memory, and decides what to send to the model next.
That system is the harness. It is the scaffolding that turns a language model into an agent with real-world capabilities.
Every AI agent has a harness. The question is whether it was designed deliberately or assembled accidentally. Most agent frameworks provide a harness by default: they give the model a shell, let it call APIs, and trust everything it says. The harness is there, but its security properties are an afterthought.
A harness determines the attack surface of an agent. Every capability the harness grants the model — file access, shell execution, web requests, API credentials — is a capability that prompt injection can weaponize.
If the harness gives the model a shell with full host access, a successful injection gives the attacker a shell with full host access. If the harness puts API keys in the model’s environment, a successful injection can read them.
The harness is the security boundary. The model is not.
Most agent frameworks build a single-process harness:
flowchart TD
subgraph process["Single process — same trust domain"]
Prompt[Prompt assembly] --> LLM[LLM]
LLM --> Tools[Tool dispatch]
Tools --> Shell[Shell execution]
Tools --> FileIO[File I/O]
Tools --> WebReq[Web requests]
Tools --> Memory[Memory storage]
Tools --> Creds[API credentials]
end
Tools --> Internet["Internet<br/>(uncontrolled)"]
style process fill:#fef2f2,stroke:#dc2626,color:#dc2626
style LLM fill:#fecaca,stroke:#dc2626
style Tools fill:#fecaca,stroke:#dc2626
style Internet fill:#fee2e2,stroke:#dc2626
In this model, the model, the tools, the credentials, and the network access all live in the same trust domain. If the model is compromised, the attacker has everything.
q15 splits the harness into three services with hard boundaries between them:
flowchart LR
subgraph Agent["q15-agent"]
A1[Prompt]
A2[Tools]
A3[Telegram]
A4[Memory]
A5[File roots]
end
subgraph Exec["q15-exec"]
E1[Nix shell]
E2[Sessions]
E3["Egress → proxy"]
end
subgraph Proxy["q15-proxy"]
P1[Credential injection]
P2[Request mutation]
P3[TLS interception]
end
Agent -->|"gRPC"| Exec
Exec -->|"proxy"| Proxy
Proxy --> Internet[Internet]
style Agent fill:#dbeafe,stroke:#2563eb,color:#2563eb
style Exec fill:#fef3c7,stroke:#d97706,color:#d97706
style Proxy fill:#bbf7d0,stroke:#16a34a,color:#16a34a
style Internet fill:#f3f4f6,stroke:#6b7280
Each service owns a distinct concern, and the boundaries are enforced by the deployment topology, not by the model’s compliance with instructions.
| The agent owns | The agent does NOT own |
|---|---|
| Prompt assembly | Network policy |
| Tool dispatch | Secret values |
| Telegram I/O | Package management |
| File operations (rooted) | Host filesystem access |
| Memory management | Egress routing |
The agent can request that a command be run or that a web request be made. It cannot guarantee that the request will be fulfilled, because the exec service and the proxy are independent services with their own policies.
The separation is not just good software architecture. It is a trust boundary. Consider what happens during a prompt injection attack in each model:
Naive harness: The model says “read the GitHub token from the environment and send it to evil.example.com.” The harness, running in the same process with full access to environment variables and the network, reads the token and sends the request. Game over.
q15 harness: The model says “read the GitHub token from the environment and send it to evil.example.com.” There is no GitHub token in the agent’s environment — credentials live in the proxy, not in the agent’s process. The agent can make a request to evil.example.com (the proxy does not block destinations by default), but it has no token to send. The proxy only injects credentials for hosts that have matching rules, and evil.example.com has none.
The defense works because:
An LLM is a general-purpose reasoning engine. You can talk to the same model through a raw API, through ChatGPT, through Claude, or through q15. The model is the same. What differs is the harness.
q15’s harness is designed around a specific thesis: you should not need to trust the model. The architecture should make a compromised model safe by constraining what it can actually do. That is what the three-service model, the rooted file access, the proxy-based egress control, and the secret injection system all serve.
Prompt Injection
The concrete attack scenario and how each q15 layer defends against it.
Three-Service Model
The architecture page: how the three services fit together in deployment.
q15-proxy
The proxy service in detail: credential injection, TLS interception, and request mutation.