AI agents

The backend an agent runs against

Tools are functions, memory is Postgres, and the agent acts as a signed-in user rather than with a key that can read everything.

agent-backend
volcano cloud functions deploy --allsearch-orders    python3.12   deployedissue-refund     python3.12   deployedagent-loop       python3.12   deployedvolcano cloud databases migration up --all -d app001_threads.sql  002_policies.sql  ok✓ tools live · memory persisted · policies on every table# the agent runs as the user who asked, not as an admin
Overview

The model is the easy part

A prototype agent is a loop and a few functions. It becomes a real system when the tools touch customer data, and the question stops being how the model reasons and starts being what it is allowed to read when it reasons on behalf of a specific person.

Volcano gives the loop somewhere to run and somewhere to remember. Tools deploy as functions that receive a verified identity, memory is ordinary Postgres tables under row-level security, and a tool called on behalf of a user can only reach that user's rows.

Tools

A tool is a deployed function

Write it in Python, Node or Ruby and deploy it with one command. There is no gateway to configure and no extra process to keep running, so adding a capability is a file and a deploy.

  • Python 3.10 through 3.14, Node 22/24, Ruby 3.3+
  • Called over HTTP or through the SDK
  • Up to 180 seconds per tool call
volcano cloud functions list
search-orders    python3.12   public   180s  invoke: https://3cd3e058.functions.volcano.run/issue-refund     python3.12   private  180s  invoke: https://7a1f92b4.functions.volcano.run/lookup-customer  python3.12   private  180s  invoke: https://b48c0d17.functions.volcano.run/agent-loop       python3.12   public   180s  invoke: https://e92d5a63.functions.volcano.run/# the invoke line is where FN_*_URL comes from# private tools refuse the anon key a browser ships with
Authorization

The agent inherits the caller

Every invocation carries a verified identity on the event, so a tool queries as the person who asked rather than with a key that can read everything. A prompt injection cannot widen what the database will return.

  • event.__volcano_auth holds the verified user
  • Their access token rides along, so a tool can call a tool
  • Blast radius is one user's data
volcano.dev/dashboard/edge-function
Agent tools deployed as functions, with their runtime and visibility
Memory

Memory is a table, not a black box

Threads, messages and summaries are rows you can query, index and delete. When a customer asks what the agent knows about them, the answer is a SELECT rather than an investigation.

  • Query history with ordinary SQL
  • Embeddings sit beside the rows they describe
  • Deleting a user's data deletes their memory
memory, inspected
select role, left(content, 34) from messages  where thread_id = 't_9c1' order by created_at;user       where is order 4821tool       search-orders → status: delayedassistant  It shipped Tuesday and is delayed3 rows · readable, auditable, deletable
Path to production

How it works

  1. 01

    Model the threads

    Threads, messages and tool calls as tables, with a user_id on each and row-level security switched on.

  2. 02

    A function per tool

    One function per capability. Read the identity off the event and let the database decide what it returns.

  3. 03

    Run the loop

    A function that calls your model, dispatches tool calls, and appends every step to the thread.

  4. 04

    Give it a face

    A Next.js app on Volcano, streaming progress over a realtime channel while the loop works.

Code

Memory, a tool, the loop, and the deploy

SQL
create table if not exists threads ( id uuid primary key default gen_random_uuid(), user_id uuid not null, title text, created_at timestamptz not null default now() ); create table if not exists messages ( id uuid primary key default gen_random_uuid(), thread_id uuid not null references threads(id) on delete cascade, user_id uuid not null, role text not null check (role in ('user', 'assistant', 'tool')), content text not null, tool_name text, created_at timestamptz not null default now() ); create index if not exists messages_thread_idx on messages (thread_id, created_at); alter table threads enable row level security; alter table messages enable row level security; -- The agent runs as the user. These policies are the whole security model. create policy own_threads on threads for all to authenticated using (user_id = auth.uid()) with check (user_id = auth.uid()); create policy own_messages on messages for all to authenticated using (user_id = auth.uid()) with check (user_id = auth.uid());
Return

What you skip

One identityfrom client to row

The user's token reaches the tool and the policy, so no tool needs its own auth logic.

3 runtimesfor tools

Python, Node and Ruby, so a tool can live wherever the library you need already is.

180sper tool call

Room for a retrieval step and a model round trip inside one invocation.

What you get

Scoped by default

A tool can only reach what the asking user could reach.

Auditable memory

Every step is a row you can read, index and delete.

Nothing to operate

No queue, no worker fleet, nothing running between turns.

Platform

Built on Volcano

Functions and agents
  • Tools and the loop

    Functions in Python, Node or Ruby, up to 180 seconds a call, each one receiving the caller's verified identity.

Databases and vector
  • Memory and retrieval

    Threads and messages as tables, embeddings in a pgvector column, and row-level security over both.

Authentication
  • Who the agent acts for

    Sessions that reach the database as auth.uid(), so the agent inherits a real user instead of an admin key.

Realtime
  • Progress while it thinks

    Broadcast each step on a channel so the UI shows real progress instead of a spinner.

Frequently asked questions

Read the docs
Does Volcano run the model?

No. You call whichever provider you use from inside a function, with the key stored as a project variable. Volcano runs the tools, the loop and the memory around it.

How do I stop a prompt injection reading another customer's data?

Connect as the caller instead of as an admin. The tool inherits that user's row-level policies, so a convincing prompt still cannot make Postgres return rows the user was never allowed to see.

What if a run takes longer than the timeout?

A single invocation is capped at 180 seconds. Persist each step as you go and continue in a follow-up call, so a long task resumes instead of restarting.

Can tools call other tools?

Yes, over HTTP. Forward the caller's token so the second tool runs as the same user instead of escalating partway through the chain. Marking a tool private stops a bare anon key reaching it, while a real session still can.

Where do embeddings go?

In a pgvector column on the table they describe. Retrieval is a normal SQL query, so it filters by tenant and permission in the same statement that ranks by similarity.

How do I show the user what the agent is doing?

Broadcast each step on a realtime channel while the loop runs. The page updates as tools are called, and nobody has to poll a status endpoint.

Ready to run your agent?

Build, deploy, and scale on Volcano's global platform — free to start, with no infrastructure to manage.

Explore more solutions