Sub-agent handoff
When an orchestrator agent spawns a sub-agent, you don’t want to ship the
master token. PerSQL handoff tokens (phand_…) are single-use, pinned to
one (database, branch, role), and expire — max 24h, default 15 min.
Orchestrator side
Section titled “Orchestrator side”import { PerSQL } from "@persql/sdk";
const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });const db = persql.database("acme/orders");
const { token } = await db.branches.pin("pr-42", { role: "write", ttlSec: 900, // 15 minutes});// → token: "phand_..."The orchestrator passes token to the sub-agent (in a tool call result, an
env var, a queue message — anywhere). The master psql_live_… never leaves
the orchestrator.
Sub-agent side
Section titled “Sub-agent side”import { PerSQL } from "@persql/sdk";
const sub = await PerSQL.fromHandoff(process.env.HANDOFF_TOKEN!);const db = sub.database(sub.handedOff.namespaceSlug, sub.handedOff.databaseSlug);
await db.query("INSERT INTO leads (email) VALUES (?)", ["a@b.co"]);fromHandoff redeems the phand_… for a regular psql_live_… token scoped
exactly to the (database, branch, role) the orchestrator pinned. Single use:
once redeemed, the handoff is consumed and another sub-agent can’t reuse it.
Why it’s safe
Section titled “Why it’s safe”- The sub-agent cannot widen scope: a
writehandoff can’t issue DDL. - It cannot escape the branch: writes go to
pr-42, not tomain. - If the sub-agent process crashes mid-task, the orchestrator just mints another handoff — no shared secret to rotate.
Next step
Section titled “Next step”Audit what the sub-agent did with db.queryLog() —
every call writes a row to _persql_meta_query_log inside the database
itself, so you can SELECT … WHERE token_id = ? from the orchestrator.