Zugriffsregeln
Lege fest, was die Besucher einer App in Datenbank, Dateien, Runs, Sessions und Freigaben ihres Environments lesen und ändern dürfen und welche Retrieval-Namespaces sie durchsuchen dürfen. Die Regeln laufen bei jeder Anfrage, und alles ohne Regel ist verboten.
Auf dieser Seite
Die Data API gibt dem Browser-Code einer App die Projektdaten ihres Environments. access in apps/<key>.yaml enthält eine Regel pro Ressource und Operation: einen Ausdruck über den Besucher und den Datensatz. Eine App ohne access kann keine Daten lesen oder ändern. Der Tab Access der App im Dashboard zeigt die deployten Regeln.
Regeln
lookups:
member:
collection: members
where: doc.email == user.email
access:
database:
tickets:
read: doc.author_id == user.id or member.role == "agent"
insert: user.signed_in == true
update: doc.author_id == user.id and doc.status == "open"
delete: member.role == "admin"
set:
author_id: user.id
status: '"open"'
files:
attachments:
read: member.role == "agent"
runs:
read: run.context.end_user.id == user.id
retrieval:
help:
search: true| Ressource | Keys | Operationen |
|---|---|---|
| database | Name der Collection | read, insert, update, delete, set |
| files | Ordner | read, write, update, delete |
| runs | read, cancel | |
| sessions | read, delete | |
| approvals | read, decide | |
| retrieval | Namespace | search |
Eine fehlende Ressource, Collection, ein fehlender Ordner, Namespace oder eine fehlende Operation ist verboten. Auch YAML-true und false sind Regeln: read: true lässt jeden Besucher lesen. Setze eine Regel, die mit einem Anführungszeichen beginnt, in Quotes, etwa read: '"finance" in user.groups', damit YAML sie als Text liest.
Namen
Jede Regel sieht user, die Lookups und den Datensatz ihrer Operation. Ein Name, der nicht verfügbar ist, lässt connic lint scheitern.
| Name | Verfügbar in | Felder |
|---|---|---|
| user | jede Regel | id, email, name, groups, claims, signed_in |
| <lookup> | jede Regel außer dem where eines Lookups | die Felder des Dokuments plus id, created_at, updated_at; null, wenn nichts gefunden wurde |
| doc | database read, update, delete | die gespeicherten Felder plus id, created_at, updated_at |
| new | database insert, update; files write, update | insert: jedes gesendete Dokument; update: das Update-Objekt; files write: path, size, mime_type, metadata des Uploads; files update: dieselben Felder nach der Änderung |
| file | files read, update, delete | path, folder, name, size, mime_type, metadata, created_at, updated_at |
| run | runs read, cancel | id, agent, status, context, input, output, queued_at, ended_at, duration_ms |
| session | sessions read, delete | id, agent, state, created_at, updated_at |
| approval | approvals read, decide | id, agent, tool, params, status, context (der Kontext des Runs), created_at |
user ist der angemeldete Besucher; was id, groups und claims in jedem Modus enthalten, steht unter Anmeldung. run.input und run.output lassen sich als JSON lesen, etwa run.input.order_id. Runs, die eine App startet, tragen den Besucher in run.context.end_user, Freigaben in approval.context.end_user.
Syntax
Regeln nutzen die Ausdruckssprache von connic, dieselbe wie Approval-Regeln und bedingte Tools: Vergleiche, and, or, not, in, not in, Pfade mit Punkten oder Subscripts und Literale, ohne Funktionsaufrufe und Arithmetik, bis zu 2.000 Zeichen. Weil connic den Teil einer Regel, der den Datensatz betrifft, in einen Abfragefilter übersetzt, gelten für die Datensatznamen doc, file, run, session und approval diese Grenzen:
- Vergleiche ein Feld mit einem Wert, etwa
doc.done == true. Ein bloßesdoc.donewird abgelehnt. - Eine Regel kann weder zwei Felder des Datensatzes noch ein Feld des Datensatzes mit
newvergleichen. doc.id,run.id,run.status,approval.idundapproval.statuserlauben nur==,!=,inundnot in.- Feldnamen dürfen nicht mit
_oder$beginnen und keine Punkte enthalten. Die gespeicherten Systemfelder heißenid,created_atundupdated_at.
Außerdem gelten für jede Regel diese Grenzen:
isundis notfunktionieren nur mit den Literalennull,trueundfalse.- Ein Vergleich kann keine andere Bedingung als Wert nehmen, deshalb wird
(doc.done == true) == falseabgelehnt. Verknüpfe Vergleiche mitand,orundnot. - Regeln für Datenbank und Dateien sowie das
wherevon Lookups werden zu Datenbankfiltern mit diesen Grenzen: 10 Verschachtelungsebenen, von denen der Filter, den connic um Zugriffsregeln legt, eine belegt, 100 Zweige proandoderor, 256 Terme und 1.000 Werte proin-Liste.
connic lint prüft Syntax und Namen. Bei öffentlichen Apps warnt das Deployment im Log vor Regeln, die jeden Besucher alles lesen lassen, Schreibzugriffe nicht vom Besucher abhängig machen oder Uploads ohne Größengrenze erlauben. Das Deployment prüft, ob connic jede Regel durchsetzen kann, und scheitert mit dem Pfad der Regel, etwa mit use is and is not only with null, true or false oder it compiles to a database filter that is too large: <reason>:
App validation failed:
- apps/portal.yaml: access.database.tickets.read: can't compare two fields of docSo wirken die Regeln
Bei jeder Anfrage wertet connic alle Teile einer Regel aus, die den gespeicherten Datensatz nicht betreffen: user, die Lookups und new. Was über den Datensatz übrig bleibt, wird zu einem Filter, den connic dem Filter des Browsers hinzufügt. Listen, Zählungen, Summen, Updates und Löschungen erreichen deshalb nur erlaubte Datensätze.
- Eine Regel, die unabhängig vom Datensatz falsch ist, lehnt die Anfrage mit 403 ab. Bei einem einzelnen Run, einer Datei, Session oder Freigabe antwortet ein Datensatz, den die Regel nicht erlaubt, mit 404, als gäbe es ihn nicht.
insertundwritebei Dateien prüfennewvor dem Schreiben. Jedes Dokument eines Inserts muss die Regel erfüllen, sonst wird nichts eingefügt.updatein der Datenbank ändert nur die Dokumente, die derdoc-Teil erlaubt, und dernew-Teil prüft das Update. Eine Regel aufnew.statusscheitert, wenn das Updatestatusnicht setzt.updatebei Dateien sieht die Datei vor der Änderung alsfileund danach alsnew. Wer eine Datei in einen anderen Ordner verschiebt, braucht zusätzlich dessenwrite-Regel.- Eine Regel kann für einen einzelnen Besucher trotzdem einen zu großen Filter ergeben, etwa
doc.team in user.groupsfür jemanden in mehr als 1.000 Gruppen. Die Anfrage scheitert dann mit 500This app's access rule makes a database filter that is too large: <reason>. Simplify the rule or the values it compares with.
Fehlende Werte und null
Ein Vergleich ist unbestimmt, wenn eine seiner Seiten fehlt, etwa ein nicht vorhandenes Feld oder ein Feld eines Lookups ohne Treffer, oder wenn sie null ist. not lässt Unbestimmtes unbestimmt. and ist falsch, sobald ein Teil falsch ist, or ist wahr, sobald ein Teil wahr ist, und sonst macht ein unbestimmter Teil das Ergebnis unbestimmt. Eine Regel, die unbestimmt endet, verbietet. not (member.blocked == true) verbietet deshalb einem Besucher ohne Member-Dokument den Zugriff.
Nur ein Vergleich mit dem Literal null prüft auf null. doc.x == null und doc.x is null sind wahr, wenn das Feld existiert und null ist, falsch, wenn es einen anderen Wert hat, und unbestimmt, wenn es fehlt; != und is not liefern für ein vorhandenes Feld das Gegenteil. Ein Lookup ohne Treffer gilt als vorhanden und null, member == null ist also wahr. doc.status != "archived" trifft nur Dokumente, deren status einen anderen Wert hat; mit or doc.status == null kommen die hinzu, bei denen er null ist.
Vergleiche
- Werte werden wie JSON verglichen. Jeder Typ ist nur sich selbst gleich:
trueist nicht1und"1"nicht1, aber1ist gleich1.0. <,<=,>und>=vergleichen zwei Zahlen oder zwei Texte, Texte nach Codepoints. Jedes andere Paar ist unbestimmt.x in listprüft die Einträge der Liste mit derselben typgenauen Gleichheit, und eine leere Liste ergibt false.x in objectprüft die Schlüssel des Objekts.x in textist nur wahr, wenn x dem ganzen Text entspricht, nie für einen Teil davon;"vip" in doc.tagspasst also zu einer Liste von Tags. In allem anderen istinunbestimmt.- Zeitstempel wie
doc.created_at,file.updated_at,run.queued_atoderapproval.created_atwerden als Zeitpunkte mit ISO-8601-Text verglichen, etwadoc.created_at >= "2026-01-01T00:00:00Z". Text ohne Zeitzonen-Offset gilt als UTC. - Ein einzelner Wert als Bedingung, etwa
user.signed_in, musstrueoderfalsesein; alles andere ist unbestimmt.
set
set listet Felder, die connic bei jedem Insert schreibt, über das hinweg, was der Browser geschickt hat. Jeder Wert ist ein Ausdruck über user und die Lookups: author_id: user.id hält fest, wer ein Dokument geschrieben hat, und status: '"open"' schreibt einen festen Text. Lässt sich ein Wert nicht auswerten, etwa weil sein Lookup nichts gefunden hat, wird der Insert abgelehnt. Ein Update, das ein set-Feld nennt, scheitert mit 400 Field 'author_id' is set by the app and can't be changed.
Lookups
Ein Lookup findet ein Dokument in einer Collection für Regeln, die von gespeicherten Daten abhängen, etwa der Rolle eines Mitglieds. collection nennt die Collection, und where ist ein Ausdruck über doc und user. connic führt einen Lookup nur aus, wenn eine Regel der Anfrage ihn nutzt, höchstens einmal pro Anfrage, und nimmt das älteste passende Dokument. Die Collection des Lookups braucht keine eigene Zugriffsregel, und der Browser kann sie nur lesen, wenn eine Regel es erlaubt.
Lookup-Namen sind Bezeichner wie member und dürfen nicht user, doc, new, file, run, session, approval, true, false oder null heißen.
Ordner
Die Keys unter files sind Ordner, mit oder ohne Schrägstriche. Eine Regel gilt für ihren Ordner und alles darunter, und der genaueste Ordner gewinnt: Mit Regeln für reports und reports/private folgen Dateien in reports/private/2026 der Regel von reports/private. Der Wurzelordner kann keine Regel haben, Dateien direkt darin bleiben also unerreichbar.
Eine Liste zeigt eine Ebene: die Dateien des Ordners, die seine Regel den Besucher lesen lässt, und die Unterordner mit mindestens einer Datei, die der Besucher nach der Regel dieser Datei lesen darf. Dateien unter einem Ordner, dessen Regel das Lesen verbietet, bleiben verborgen, außer ein genauerer Ordner darunter hat eine Regel, die sie erlaubt. Ein Ordner ohne eigene Regel, etwa der Wurzelordner, liefert keine Dateien, aber die Unterordner mit lesbaren Dateien. Nur wenn keine Regel auf oder unter dem Ordner das Lesen erlauben kann, wird die Liste mit 403 abgelehnt.
Retrieval-Namespaces
Die Keys unter retrieval sind Namespaces in Retrieval, etwa help oder docs.public. Eine Regel search gilt für ihren Namespace und alle Namespaces darunter, denn eine Suche liefert Textstellen aus allen. Die Regel sieht user und die Lookups, nicht die Einträge. Sie entscheidet also, ob der Besucher den ganzen Namespace durchsuchen darf.
Ein genannter Namespace darf nicht in einem anderen genannten liegen: Neben help lässt help.internal das Deployment mit help already covers it; declare only one of them scheitern. Lege Inhalte für unterschiedliche Zielgruppen in getrennte Namespaces, etwa public.help und internal.help. Einträge ohne Namespace lassen sich aus einer App nicht durchsuchen.
Öffentliche Besucher
In einer öffentlichen App ist user.signed_in false, user.email und user.name sind null, user.groups ist leer, und user.id ist die Besucher-ID des Browsers.
Vergleiche mit der E-Mail-Adresse eines öffentlichen Besuchers sind unbestimmt. doc.owner == user.email trifft deshalb kein Dokument, auch keines, dessen owner null ist, und ein Lookup mit where: doc.email == user.email findet nichts.
connic leitet die Besucher-ID für jede App getrennt aus einem Secret im Cookie des Browsers ab. Wer die ID eines anderen kennt, kann also nicht in dessen Namen handeln. Die ID ändert sich, wenn jemand die Cookies löscht, und jeder kann sich eine neue holen. Regeln auf user.id passen zu Daten, die zu einem Browser gehören, etwa einem Entwurf, nicht zu Daten, die ein Konto brauchen.
Öffentliche Formulare schützen
Die Formulare einer öffentlichen App kann jeder absenden, auch Bots. Schreibzugriffe über die Data API prüft connic gegen deine Regeln, aber ohne Middleware, eine Challenge lässt sich dort also nicht prüfen. Für Formulare, die Bots anziehen, schützt du das Formular selbst und lässt einen Agenten schreiben:
Binde einen Challenge-Dienst wie ein CAPTCHA in das Formular ein. Er fügt dem Absenden ein Token hinzu.
Schicke das Formular an einen Sync-Webhook-Connector mit
apps_only: trueunter/_connic/c/<key>, statt es über die Data API einzufügen.Prüfe das Token in der Middleware des Agenten beim Challenge-Dienst und stoppe den Run, wenn die Prüfung scheitert. Danach schreibt der Agent mit Database Tools.
Lass
insertin den Regeln der Collection weg, damit der Browser nicht an der Prüfung vorbei schreiben kann.
import os
import httpx
from connic import StopProcessing
async def before(content, context):
token = context["payload"].get("challenge_token")
async with httpx.AsyncClient(timeout=10) as client:
response = await client.post(
os.environ["CHALLENGE_VERIFY_URL"],
data={"secret": os.environ["CHALLENGE_SECRET"], "response": token},
)
if not response.json().get("success"):
raise StopProcessing("Die Formularprüfung ist fehlgeschlagen. Lade die Seite neu und versuche es noch einmal.")
return contentWie die Prüfung aussieht, hängt vom Challenge-Dienst ab; das Beispiel schickt das Token und ein Secret an die Prüf-URL des Dienstes, beide als Variablen gespeichert. StopProcessing beendet den Run, bevor der Agent startet, und das Formular bekommt die Meldung als Ausgabe des Runs. Ein rate_limit am Connector begrenzt, wie viele Runs das Formular pro Minute starten kann. connic prüft Challenges nicht selbst.
Beispiele
Eigene Datensätze
Angemeldete Besucher sehen und ändern nur die Notizen, die sie angelegt haben. set trägt beim Insert den Besitzer ein, und Updates können ihn nicht ändern.
access:
database:
notes:
read: doc.owner_id == user.id
insert: user.signed_in == true
update: doc.owner_id == user.id
delete: doc.owner_id == user.id
set:
owner_id: user.idAdmins über einen Lookup
In einer App mit Anmeldung hält eine Collection members, gepflegt von Agenten oder importiert, Kunde und Rolle jeder Person. Kunden sehen ihre eigenen Bestellungen, Admins sehen und ändern alle.
lookups:
member:
collection: members
where: doc.email == user.email
access:
database:
orders:
read: doc.customer_id == member.customer_id or member.role == "admin"
update: member.role == "admin"Veröffentlichte Inhalte in einer öffentlichen App
Jeder kann veröffentlichte Artikel lesen. Entwürfe bleiben verborgen, und niemand kann schreiben.
auth:
mode: public
access:
database:
articles:
read: doc.status == "published"Dateien nach Gruppe und Metadaten
Die Gruppe billing liest Rechnungen. Jeder Besucher lädt PDFs bis 10 MB nach uploads hoch, markiert mit seiner eigenen ID aus /_connic/me, und sieht nur seine eigenen Uploads.
access:
files:
invoices:
read: '"billing" in user.groups'
uploads:
read: file.metadata.uploaded_by == user.id
write: >-
new.metadata.uploaded_by == user.id
and new.mime_type == "application/pdf"
and new.size <= 10000000Runs, die der Besucher gestartet hat
Besucher verfolgen die Runs, die sie über die Connectors der App gestartet haben, und brechen sie ab, solange sie warten oder laufen.
access:
runs:
read: run.context.end_user.id == user.id
cancel: run.context.end_user.id == user.id and run.status in ["queued", "started"]Freigaben für eine Gruppe
Die Gruppe finance sieht jede Freigabe des Environments und entscheidet über Erstattungen unter 5.000.
access:
approvals:
read: '"finance" in user.groups'
decide: '"finance" in user.groups and approval.tool == "issue_refund" and approval.params.amount < 5000'Suche im Help Center
Jeder kann die Artikel in help und den Namespaces darunter über retrieval/search durchsuchen. In einer App mit Anmeldung beschränkt eine Regel wie search: '"support" in user.groups' die Suche auf eine Gruppe.
auth:
mode: public
access:
retrieval:
help:
search: true