Give every agent its own expiring database role
A dedicated role per agent, limited to the permissions its task needs and set to expire, makes access easy to reason about and easy to end.
The fastest way to connect an agent to a database is to give it the owner password. It is also the hardest to undo: every agent shares one credential, you cannot revoke one without breaking the others, and the database cannot tell you which agent ran which statement.
A better default is one database role per agent, created for its task and set to expire when the task should be over. This guide shows how to do that with the Lymeno API.
Create a role for the task
curl -X POST https://console.lymeno.com/v1/databases/$DATABASE_ID/roles \
-H "Authorization: Bearer $LYMENO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "agent_report_0412",
"inheritedRoles": ["pg_read_all_data"],
"validUntil": "2026-10-04T00:00:00Z"
}'
The request takes three fields:
username: 3–63 characters, starting with a lowercase letter and containing only lowercase letters, digits, and underscores. Names reserved by PostgreSQL or Lymeno, such aspostgres,admin, and names that start withpg_, are rejected.inheritedRoles: the permissions the role receives. At least one is required.validUntil: an optional expiration time in ISO 8601 format, which must be in the future. If you omit it, the role does not expire.
The response contains the role, the ID of the operation that creates it, and the password. The password is returned only in this response. Store it where the agent can read it, such as the agent’s secret store, before you do anything else.
Grant only what the task needs
inheritedRoles accepts a fixed set of values:
| Role | What it allows |
|---|---|
owner |
Owns the public schema; can create and change tables |
pg_read_all_data |
Read all tables, views, and sequences |
pg_write_all_data |
Insert, update, and delete in all tables |
pg_read_all_stats |
Read all statistics views |
pg_monitor |
Read monitoring views and functions |
pg_signal_backend |
Cancel queries and end sessions of other roles |
pg_maintain |
Run VACUUM, ANALYZE, REINDEX, and similar maintenance commands |
Every role except owner is a PostgreSQL built-in role with its standard meaning. A reporting agent needs pg_read_all_data and nothing else. A migration agent needs owner. Agents that only watch for slow queries need pg_monitor.
On-demand databases currently support owner, pg_monitor, and pg_signal_backend. Other values return validation_failed with the reason role_not_supported.
Let access end on its own
validUntil maps to PostgreSQL’s VALID UNTIL. After that time, PostgreSQL rejects new logins for the role, so access ends even if nobody remembers to clean up.
Two details are worth knowing:
- Expiration applies to new connections. Sessions that are already open keep running until they disconnect. If an agent holds a long-lived connection pool, close it when the task ends.
- An expired role still exists. Delete it with
DELETE /v1/databases/:id/roles/:roleIdwhen you no longer need it. Each database can have up to 20 roles.
Pair roles with scoped API tokens
The database role controls what an agent can do inside PostgreSQL. The API token controls what it can do in Lymeno. Apply the same principles to both:
- Create a token for each agent or automation, not one token for everything.
- Give tokens that only read state the read-only
memberrole, and giveadminonly to tokens that need to make changes. - Set tokens to expire after 30, 90, or 365 days.
- API tokens cannot create or revoke other tokens, so a leaked token cannot issue new ones.
Every change made with a token is recorded in the audit log under that token’s name. When each agent has its own token and its own role, the audit log and the database’s own logs tell the same story: which agent did what, and when.