Reading eleven pages about a distributed system is a poor way to understand it. Watching one message cross it is a good one.
In this lesson you will send a single question, watch it pass through five services, and then read the record it left behind. You need the stack running and a non-admin account — see Get it running.
Leave this running in one terminal. It is every service's log, interleaved:
In the browser, open the Tamebi app as your test user and ask something that will certainly need a tool:
What files do I have in Contracts?
The chat calls a Nextcloud route, authenticated the way every Nextcloud page is — a session cookie and a CSRF token. Nothing about the agent is reachable from the browser directly.
This is the only component in the system with a Nextcloud session, so it is the only one that can answer "who is this". It reads the username from the session object — never from anything the browser sent as data — and signs it together with a timestamp and a hash of the request body.
Everything downstream verifies that identity and no component re-derives it. A username arriving in a request body is rejected with a 400 rather than ignored, because a body username is an attempt to choose one.
In the log you will see the turn start. Before the model sees anything, the orchestrator has:
Notice what it did not do: look anything up in Nextcloud. It holds no credential for it. Everything it knows about your workspace, it will learn from tool results during this turn.
The response starts streaming immediately — text and tool events, on one channel.
In the log you will see the call reach mcp-nextcloud, and a WebDAV request leave
it towards Nextcloud carrying your token.
Try this now, in a third terminal, and compare:
There is no username in that service's configuration. It has no account. The uid it logs came out of the credential on the request it is currently serving, which is why one stateless process can serve everyone.
The tool result does not enter the conversation as text. It enters inside an envelope that marks it as data:
Every external string goes through that one chokepoint — tool results, compaction summaries, and remembered facts alike, because all three are things other people can write into. The prompt tells the model the contract: content inside those tags is information to reason about, never a command, and text in there asking it to call a tool is an attack to report.
Every call landed in the audit database. Look at your own turn:
You are looking at the feedback loop on the tool surface. category says whether
the call was reading or acting. failure_kind distinguishes Nextcloud refused —
which is the system working — from the model could not use the tool, which means
a description or a schema needs to change.
Now do the interesting half. Ask for something you are not allowed to see:
Read /admin/notes.md
The answer will be some version of "that does not exist or you cannot see it". Look at the audit row:
failure_kind is permission or not_found, and the error text is an instruction
aimed at the model — this is a permission decision, not a transient error; do not
retry. Two things did not happen: no code of ours checked whether you were
allowed, and no code of ours tried a different path to get the file anyway.
That is what "there is no permission logic in this codebase" means in practice. The refusal came from Nextcloud and was passed along intact.
Ask for something with an effect:
Create a card "Renew the contract" on my board.
The stream stops and the interface asks. What is happening underneath is worth knowing, because it looks like UI and is not:
readOnlyHint: false. The policy derived from that annotation
is needsApproval, computed in the orchestrator, carried on the tool itself.Approve it, then look at the audit row: category=action, approved_by_user=1.
You have seen the four properties the rest of these pages argue about, in the order they occur:
| Where | What is true there |
|---|---|
| PHP | identity is decided once, from a session, and signed |
| orchestrator | holds the model key and no workspace credential |
| MCP server | holds no credential at all; spends yours |
| Nextcloud | the only thing that decides what is permitted |
The reasoning behind each is in The security model. What it cost to make each one true, and what it was chosen over, is in Decisions.