Skip to content
Decision5 min read

Giving an AI assistant tools without giving it the keys

The safest way to let a model act on your system is to make it use the same front door as the user, carrying the user's own credentials.

AISecurityArchitecture
From the work onPROMAn AI-native operating system for product development

Every product that gives a language model tools eventually hits the same question: what is the model actually allowed to touch? The tempting answer is a service account. Give the AI layer a privileged connection, let it do what it needs, and filter the results afterwards.

I think that answer is wrong, in a way that is hard to see until it bites. A service account means your authorisation rules now exist twice — once in the checks your application already performs, and once in prompt instructions and post-hoc filtering. The second copy is written in English, evaluated by a probabilistic system, and drifts the moment someone adds a permission.

The alternative is no new privileges at all

The approach I settled on: the AI layer holds no database credentials whatsoever. When the model decides to create something, it does not write a row. It calls the ordinary application API — the same one the web client uses — carrying the credentials of the human it is working for.

text
user ──credentials──▶ app API ──service token──▶ AI layer
                                            │
                            tool call       │  (carries the USER's credentials)
                                            ▼
                                        app API
                             rate limit → identity → permissions
                                            ▼
                                        handler

The consequence is structural rather than procedural. Every check already in the request path applies unchanged. If the human cannot delete that record, neither can the model — not because a prompt told it not to, but because the request comes back 403.

What it costs

  • Latency. A tool call becomes a real HTTP round trip rather than an in-process function call. Against a model call measured in seconds, this disappears.
  • Credential lifetime. The user's token has to stay valid across an agent loop that can run longer than a normal request, so refresh handling has to be deliberate rather than incidental.
  • Error surface. The model now encounters 402s, 403s and 429s. Those need to become sentences it can reason about, not raw status codes.

The failure this actually prevents

The risk here is not the dramatic one. Nobody's assistant drops the production database. What actually happens is quieter: a user asks a broad question, a retrieval step running with elevated credentials pulls in a document belonging to someone else, and the model helpfully summarises it. No alarm fires. Nothing looks broken.

That is a tenancy bug, and tenancy bugs are the kind that end companies. Making the AI use the user's own credentials means the cross-tenant read is impossible for the same reason it is impossible in the web app: the query never had access to begin with.

If you take one thing

Do not build a second authorisation system for your AI features. You already have one, it is already tested, and it is already the thing you patch when someone reports a bug. Route the model through it and let it inherit every guarantee you have already paid for.

Next

A dependency changed underneath me and I had shipped no commit