OAKEN CORE
Alpha

Prompt safety

Outside text is data. It enters a prompt only inside a fenced block with a fresh random nonce. The instructions are written in code. The answer is JSON that is checked, never run.

1

The rule

An item can say anything, including "ignore your instructions". So core keeps item text away from the instructions:

2

The worked example: the llm triager

repo:src/oaken_core/triage/llm.py is the pattern. It asks a model whether an item meets the owner's criteria.

A plugin asks through BaseLLMAnalyzer.ask(), which hands the request to the router.

3

Structured answers

The router validates every reply against the plugin's answer model. A reply that is not JSON, or does not fit, counts as a bad answer. It gets one escalation to a larger model, if the route names one. Then the call raises AIBackendError and stores nothing. Error text names the fields that were wrong (never the input or the reply). Tokens spent on a bad answer are still counted in the ledger.

Output is capped ([router] default_output_tokens, 256 unless the request sets its own). Input is cut to the backend's context window (and to default_input_tokens, 2000). Later parts are cut first.

4

Confidential items

An item labelled confidential = "true" (a reserved label) must stay off remote machines. The router reads each backend's locality from its table, [plugins.llm_backend.<name>]:

Locality A confidential call may use it
local Always.
lan Only if its name is in [router] lan_allowlist.
hosted Never.

A backend that does not say counts as hosted. So does a locality core does not know (it logs an error). A disallowed backend is skipped, including in fallback and escalate. If none is left, the router raises ConfidentialRefused. It never falls back to a remote box. The same rule covers the embedder.

[router]
lan_allowlist = ["desk"]

[plugins.llm_backend.desk]
plugin = "ollama"
locality = "lan"
5

What this does not prove

Delimiters, nonces and a schema make an attack harder and make a success visible. They do not make a model obey. A model can still be talked into a wrong answer, which is why answers are validated, never executed, and never trigger an action by themselves. The unit tests check what is sent and what is refused (tests/test_untrusted.py, tests/test_triage_llm.py). They cannot show how a given model behaves when it reads hostile text. That needs a live run against a real model. Core does not yet lint plugin source for prompts built from raw strings (oaken-mail does); a plugin author keeps outside text out of system by hand.

The answer cache holds text derived from items, so treat it as sensitive as the items. cache_clear removes it, all of it or one analyzer's.