In connic, apps and agents write access rules the same way. An app's rules decide what a visitor's browser may read and change. An agent's rules decide what its built-in database, file, and Retrieval tools may do. When an app visitor started the run, the agent's rules can limit it to that visitor's records, and the agents it calls see the same visitor in their own rules. This tutorial sets up both for a customer portal and moves agents off the older database, retrieval, and files settings.
One rule language, two defaults
Rules sit under access in apps/<key>.yaml or agents/<name>.yaml. Each rule belongs to a resource, such as a database collection, a files folder, or a Retrieval namespace, and to an operation. It holds an expression about who asks and about the record, written in connic's expression language, the same one that approval rules and conditional tools use. Database and files rules have the same operations in apps and agents. In Retrieval, apps can only search, while agents can also store and delete. Only apps have rules for runs, sessions, and approvals.
The two differ in what happens without rules:
access can't read or change any data.connic.tools. An agent without access or the older settings can use all data of its environment.Once an agent has access, it is as strict as an app: a collection, folder, namespace, or operation without a rule is denied. If the agent behind an app's chat has no rules, the app shows each customer only their own orders, while the agent answers the same customer with a database tool that can read every order.
Example: an order portal
The example is an order portal for a fictional online shop. Customers sign in with the shop's own login, see their orders, upload photos of damaged goods, and ask the agent order-support questions in a chat. The chat runs through a WebSocket connector with apps_only: true. A members collection links each customer's email to a customer_id, and every order and payment carries that ID.
The app's rules use a lookup to find the visitor's entry in members and show only that customer's orders. Uploads go to the uploads folder, and set writes the visitor's ID into each file's metadata, whatever the browser sends:
connectors:
- portal-chat
lookups:
member:
collection: members
where: doc.email == user.email
access:
database:
orders:
read: doc.customer_id == member.customer_id
files:
uploads:
read: file.metadata.uploaded_by == user.id
write: new.size <= 10000000
set:
uploaded_by: user.idOrders have no insert, update, or delete rule, so the browser can't change them. Each visitor sees only the files tagged with their own ID. Sign-in, connectors, and the data API are covered in the apps announcement.
Hold the agent to the visitor's records
A chat message from the portal starts a run that carries the visitor. connic takes the visitor from the signed app request, so middleware, tools, and the message itself can't change it, and the agent's rules see it as user. The agent file gets the same lookup as the app and rules for its database tools. The file and Retrieval rules follow in the next section:
name: order-support
tools:
- db_find
- db_count
- db_update
- file_list
- file_read
- file_write
- retrieval_query
- trigger_agent
lookups:
member:
collection: members
where: doc.email == user.email
access:
database:
orders:
read: user == null or doc.customer_id == member.customer_id
update: user == null or doc.customer_id == member.customer_idconnic evaluates user and the lookup first and adds the rest of the rule to the tool's query. For a signed-in customer, db_find and db_count return only their orders, and db_update changes only those. Asked about another customer's order number, the agent finds nothing, as if the order didn't exist.
user == null covers runs without a visitor, such as runs from cron, a webhook, or the dashboard. They still reach every order. Without that condition, the rules deny those runs.
When a rule can't hold for any record, for example for a visitor without an entry in members, the tool call returns an access-denied error instead of a result. The run's logs record it as a warning. Every database, file, or Retrieval tool the agent lists needs rules as well: once access is set, connic lint fails for any of them whose resource has no entries, because every call would be denied.
Files and Retrieval namespaces
The same access block holds rules for the agent's file and Retrieval tools:
access:
files:
uploads:
read: user == null or file.metadata.uploaded_by == user.id
write: true
set:
uploaded_by: user.id
retrieval:
help:
search: trueThe agent tags files in uploads with the same uploaded_by as the app. In the portal, a customer sees the files the agent saved for them, and the agent reads only that customer's photos. Because set reads user, runs without a visitor can't write to uploads. Without update and delete rules, the agent can't rename, move, or remove files.
In Retrieval, the agent searches help and the namespaces below it. A search without a namespace would need an entry for "*", and storing or deleting entries is denied. A search in help returns passages from namespaces such as help.internal as well, whatever their own rules say. Content for staff therefore belongs in a separate namespace, such as internal.help.
Child agents work for the same visitor
For refund questions, order-support starts the agent refund-check with trigger_agent. connic passes the visitor on to that run, so the rules of refund-check see the same user, and so do the agents it starts in turn. The rules themselves stay with each agent: refund-check applies its own access block with its own lookups.
name: refund-check
tools:
- db_find
lookups:
member:
collection: members
where: doc.email == user.email
access:
database:
orders:
read: user == null or doc.customer_id == member.customer_id
payments:
read: user == null or doc.customer_id == member.customer_idA child agent without access can use all data of its environment, even while it works for a visitor. Every agent that a visitor's run can reach through trigger_agent needs rules of its own. Only runs the app starts carry the visitor in end_user, so the run context of refund-check doesn't include it. If refund-check needs the customer's name for its answer, order-support passes it in the payload.
Replace the older database, retrieval, and files settings
Before access, agents limited their tools with the database, retrieval, and files settings. They are deprecated but still work: connic enforces them as access rules, and deploys warn about them in the log. An agent can't combine them with access. A typical older configuration:
database:
collections:
orders:
prevent_delete: true
files:
prevent_delete: trueconnic lint warns about it and prints an access block with the same behavior:
! agents/order-support.yaml: database, retrieval and files are deprecated; replace them with:
access:
database:
orders:
read: true
insert: true
update: true
files:
'*':
read: true
write: true
update: true
retrieval:
'*':
search: true
store: true
delete: trueThe block also spells out what the older settings allowed. The agent never set retrieval, so it could search, store, and delete in every namespace, and prevent_delete on files still let it write to every folder. Pasting the block in place of the old settings keeps the agent working exactly as before. A second edit then narrows it to the collections, folders, and namespaces the agent needs, with conditions on user where it works for app visitors. The access rules documentation maps each older setting to its rules.
Check runs with and without a visitor
Lint the project. With composer SDK 0.1.63 or later,
connic lintshows no deprecation warning and no database, file, or Retrieval tool without rules.Sign in as two customers. Use two test accounts whose entries in
memberspoint to different customers. Each sees only their own orders, in the portal and in the agent's answers. Asking about the other customer's order number finds nothing.Sign in without a member entry. The agent's database calls return an access-denied error, and the run's logs show a warning for each.
Run the agent without a visitor. Started from the dashboard or by cron,
order-supportreaches every order, and writes touploadsare denied.Follow a child run. Ask about a refund in the portal and open the run of
refund-checkfrom the parent's trace. Its database calls return only that customer's orders and payments.
Give every customer only their own data
Build your agents and the app that calls them in one connic project, with access rules in code.