diff --git a/api-reference/v2/endpoint/connectors-overview.mdx b/api-reference/v2/endpoint/connectors-overview.mdx index 55a3d022..e759091c 100644 --- a/api-reference/v2/endpoint/connectors-overview.mdx +++ b/api-reference/v2/endpoint/connectors-overview.mdx @@ -25,7 +25,7 @@ Check on it: Change it: -- [Update Connector](/api-reference/v2/endpoint/update-connector): `PATCH /connectors/{id}` changes the sync interval, connector-level instructions or credentials. +- [Update Connector](/api-reference/v2/endpoint/update-connector): `PATCH /connectors/{id}` changes the sync interval, connector-level instructions, credentials or the ACL enforcement setting. - [Update Connector Resource](/api-reference/v2/endpoint/update-connector-resource): `PATCH /connectors/{id}/resources/{resource_id}` changes one resource's instructions or access rule. - [Add Connector Resource](/api-reference/v2/endpoint/add-connector-resource): `POST /connectors/{id}/resources` adds one new resource. - [Delete Connector Resource](/api-reference/v2/endpoint/delete-connector-resource): `DELETE /connectors/{id}/resources/{resource_id}` stops syncing a resource. @@ -37,7 +37,7 @@ Change it: 2. `GET /connectors/{id}/discover` to see which resources are available. 3. `POST /connectors/{id}/configure` with the resources to sync. This starts the first sync. 4. `GET /connectors/{id}/status` until `status` is `healthy` and `lifecycle` is `active`. -5. `POST /query` to search the database and collection chosen during configuration. For restricted content, send the caller’s identity as `acl`; omitting it leaves results unfiltered. `query_apps` is enabled by default. +5. `POST /query` to search the database and collection chosen during configuration. For content from connectors without ACL enforcement, send the caller's identity as `acl`; omitting it leaves those results unfiltered. Enforced connectors use the authenticated caller instead. `query_apps` is enabled by default. After that, syncs run every `sync_interval_seconds` (one hour by default). Call `POST /connectors/{id}/sync` only when you need data sooner. @@ -46,7 +46,7 @@ After that, syncs run every `sync_interval_seconds` (one hour by default). Call To change a resource that is already configured, use `PATCH /connectors/{id}/resources/{resource_id}` or `POST /connectors/{id}/configure`. `POST /connectors/{id}/resources` adds new resources only: on an existing `resource_id` it returns `409` and changes nothing. - Instructions or access rule on one resource: `PATCH /connectors/{id}/resources/{resource_id}`. Only the fields you send change. -- Instructions, sync interval or credentials for the whole connector: `PATCH /connectors/{id}`. Only the fields you send change. +- Instructions, sync interval, credentials or ACL enforcement for the whole connector: `PATCH /connectors/{id}`. Only the fields you send change. Only workspace owners and writers in a signed-in dashboard session can set `enforce_acl`; readers can view the current setting but cannot change it, and API keys cannot set it. - Name, type, database, collection or metadata of resources: `POST /connectors/{id}/configure`. Resources you leave out of the list are not touched. For each resource you list, send `name` and `resource_type` every time, because omitted values are cleared. Its sync position, instructions and access rule are kept. - A brand new resource: `POST /connectors/{id}/configure` or `POST /connectors/{id}/resources`. @@ -54,13 +54,15 @@ In paths, `{resource_id}` is the resource's id as returned by the Discover and L ## Authentication -All connector endpoints use your HydraDB API key: +Connector API requests use your HydraDB API key: ```http Authorization: Bearer $HYDRA_DB_API_KEY API-Version: 2 ``` +To set `enforce_acl`, use a signed-in dashboard session for a workspace owner or writer instead; API-key-authenticated requests cannot set it. + ## Key concepts - **Connector:** an authenticated connection to one external provider account. It owns every resource synced from that account. diff --git a/api-reference/v2/endpoint/create-connector.mdx b/api-reference/v2/endpoint/create-connector.mdx index be5ec350..c40f1e13 100644 --- a/api-reference/v2/endpoint/create-connector.mdx +++ b/api-reference/v2/endpoint/create-connector.mdx @@ -12,6 +12,7 @@ Optional settings you can also pass here, and change later with [Update Connecto - `custom_instructions`: guidance for how this connector's documents are interpreted and indexed. See [Custom Ingestion Instructions](/essentials/v2/connectors#custom-ingestion-instructions). - `sync_interval_seconds`: how often scheduled syncs run. Omit it for the provider default (one hour for most providers). +- `enforce_acl`: whether content reads always enforce ACL for the authenticated caller for this connector. It defaults to `false` when omitted. Only workspace owners and writers in a signed-in dashboard session can set it, and `true` requires the provider to report `acl_supported: true`. API-key-authenticated requests must omit this field. An owner or writer can enable or change it later with [Update Connector](/api-reference/v2/endpoint/update-connector) or in the dashboard. See [ACL on synced content](/essentials/v2/connectors#enforce-acl) for how caller identity and request `acl` behave. @@ -70,6 +71,7 @@ curl -X POST 'https://api.hydradb.com/connectors' \ "provider_account_scope": "T12345ACME", "lifecycle": "pending_setup", "sync_status": "idle", + "enforce_acl": false, "sync_interval_seconds": 3600, "next_sync_at": "2026-06-01T12:05:00Z", "first_sync_at": "2026-06-01T12:05:00Z", @@ -83,7 +85,7 @@ curl -X POST 'https://api.hydradb.com/connectors' \ - `400`: `provider` or `database` is missing, the provider is unknown, or a setting such as `sync_interval_seconds` is out of range. - `402`: your plan's connector limit is reached. -- `403`: the provider is not available to your organization. +- `403`: the provider is not available to your organization, or the request tries to set `enforce_acl` without a signed-in dashboard session for a workspace owner or writer. The ACL authorization error message is `only workspace owners and writers can change ACL enforcement`. - `422`: `credentials` does not match the provider's `credential_schema`.
diff --git a/api-reference/v2/endpoint/fetch-content.mdx b/api-reference/v2/endpoint/fetch-content.mdx index 8ae108a8..e1107486 100644 --- a/api-reference/v2/endpoint/fetch-content.mdx +++ b/api-reference/v2/endpoint/fetch-content.mdx @@ -49,7 +49,7 @@ curl -G 'https://api.hydradb.com/context/inspect' \ | | Collection scope. If omitted, the default collection is used. | | | What to return. See [Fetch modes](#fetch-modes). (default=`"both"`) | | | TTL of the presigned URL (when `mode` includes `url`). Range `60` to `604800` (7 days). (default=`3600`) | -| | Principals to answer as (document ACLs). A source they cannot see returns the same `404` as a missing one. Repeated (`acl=a&acl=b`) or comma-separated. Omit for no ACL scoping. See [Access Control](/essentials/v2/access-control). | +| | Principals to answer as (document ACLs). A source they cannot see returns the same `404` as a missing one. Repeat or comma-separate values. Omit for no caller-supplied ACL filtering. For enforced connectors, the authenticated caller is used instead; see [ACL enforcement](/essentials/v2/connectors#enforce-acl). | ## Fetch modes diff --git a/api-reference/v2/endpoint/list-connector-providers.mdx b/api-reference/v2/endpoint/list-connector-providers.mdx index 6db3ea21..b3790b64 100644 --- a/api-reference/v2/endpoint/list-connector-providers.mdx +++ b/api-reference/v2/endpoint/list-connector-providers.mdx @@ -53,6 +53,8 @@ TypeScript SDK 2.1.7 requires the explicit authorization header for this method. "supported": true, "moveit_support": false, "webhook_support": false, + "acl_supported": true, + "acl_description": "Captures channel-level access: public channels are visible to the whole workspace, private channels only to their members.", "is_alpha": false, "is_beta": false, "rank": 1 @@ -94,6 +96,8 @@ TypeScript SDK 2.1.7 requires the explicit authorization header for this method. Only providers you can connect are listed, so `supported` is always `true`. `webhook_support` is `true` for providers whose data arrives by webhook instead of scheduled syncs. `rank` is `null` for providers without a display position; they are listed last, alphabetically. +`acl_supported` indicates whether the provider captures ACL as document access rules. When it is `true`, `acl_description` summarizes the captured ACL. `enforce_acl` can only be enabled for a connector when its provider reports `acl_supported: true`; see [ACL on synced content](/essentials/v2/connectors#enforce-acl). + ## One provider - `credential_schema` is the JSON Schema for `credentials` on [Create Connector](/api-reference/v2/endpoint/create-connector) and [Update Connector](/api-reference/v2/endpoint/update-connector). It is omitted when unavailable. diff --git a/api-reference/v2/endpoint/list-documents.mdx b/api-reference/v2/endpoint/list-documents.mdx index 5cbf3447..5f9ef6dc 100644 --- a/api-reference/v2/endpoint/list-documents.mdx +++ b/api-reference/v2/endpoint/list-documents.mdx @@ -76,7 +76,7 @@ curl -X POST 'https://api.hydradb.com/context/list' \ | | Items per page, from `1` to `100`. (default=`50`) | | | Structured exact-match filters. See [Filters](#filters). (default=`null`) | | | Field projection. Only the listed fields plus `id`, `database`, `collection` are populated. Only applies to `type=knowledge`. (default=`null`, all fields) | -| | Principals to list as (document ACLs). Only knowledge sources those principals may see come back. Omitted, `[]`, or `["*"]` lists everything. Ignored for `type=memory`. See [Access Control](/essentials/v2/access-control). | +| | Principals to list as (document ACLs). Omitted, `[]`, or `["*"]` disables caller-supplied filtering. Ignored for `type=memory`. For enforced connectors, the authenticated caller is used instead; see [ACL enforcement](/essentials/v2/connectors#enforce-acl). | ### Filters diff --git a/api-reference/v2/endpoint/query.mdx b/api-reference/v2/endpoint/query.mdx index 705b5705..acd4fcec 100644 --- a/api-reference/v2/endpoint/query.mdx +++ b/api-reference/v2/endpoint/query.mdx @@ -316,7 +316,7 @@ result = client.query( | | Multi-collection scope. Send a list of collection IDs for equal weighting, or an object mapping collection ID to a positive relative weight (at most one decimal place, e.g. `{"finance": 1.5, "legal": 0.8}`) to bias ranking. Up to 100 collections. Do not combine with `collection`. | | | Query terms or natural-language question. Cannot be empty. Maximum 5,000 words. | | | Exact source titles, matched case-insensitively. Matches any listed title, then intersects with `ids` if both are sent. | -| | Caller identities used to enforce document permissions. Omitting `acl`, sending `[]`, or sending `["*"]` disables permission filtering. See [Access Control](/essentials/v2/access-control). | +| | Caller identities used to filter document ACLs. Omitting `acl`, sending `[]`, or sending `["*"]` disables caller-supplied filtering. For enforced connectors, the authenticated caller is used instead; see [ACL enforcement](/essentials/v2/connectors#enforce-acl). | | | Omit for the result object with `llm_prompt`. `"structured"` leaves out the prompt string. `"markdown"` returns the prompt string directly as `data`. | | | What to query. `"all"` runs knowledge and memory in parallel over the same scope and merges by `relevancy_score`. (default=`"knowledge"`) | | | Retrieval method. See [Query methods](#decision-matrix). (default=`"hybrid"`) | @@ -348,7 +348,7 @@ Metadata filters work like SQL `WHERE` clauses. Top-level `metadata_filters` key } ``` -See [Metadata and Filters](/essentials/v2/metadata#3-filter-operators) for operators and examples. Filters narrow results. They do not replace the caller's `acl`. +See [Metadata and Filters](/essentials/v2/metadata#3-filter-operators) for operators and examples. Filters narrow results. They do not replace the caller's `acl` or connector ACL enforcement. ### Decision matrix diff --git a/api-reference/v2/endpoint/source-relations.mdx b/api-reference/v2/endpoint/source-relations.mdx index 5cec5268..63b9a6df 100644 --- a/api-reference/v2/endpoint/source-relations.mdx +++ b/api-reference/v2/endpoint/source-relations.mdx @@ -49,7 +49,7 @@ curl -G 'https://api.hydradb.com/context/relations' \ | | Collection scope. If omitted, the default collection is used. | | | Maximum relation groups to return. Range `1` to `10000`. (default=`5000`) | | | Opaque pagination cursor from a previous response's `next_cursor`. (default=`null`) | -| | Principals to answer as (document ACLs). Only relations from sources those principals may see are returned. Repeated (`acl=a&acl=b`) or comma-separated. Omit for no ACL scoping. See [Access Control](/essentials/v2/access-control). | +| | Principals to answer as (document ACLs). Only relations from sources those principals may see are returned. Repeat or comma-separate values. Omit for no caller-supplied ACL filtering. For enforced connectors, the authenticated caller is used instead; see [ACL enforcement](/essentials/v2/connectors#enforce-acl). | diff --git a/api-reference/v2/endpoint/subgraph.mdx b/api-reference/v2/endpoint/subgraph.mdx index a8ff92ad..0449c790 100644 --- a/api-reference/v2/endpoint/subgraph.mdx +++ b/api-reference/v2/endpoint/subgraph.mdx @@ -56,7 +56,7 @@ curl -G 'https://api.hydradb.com/context/slack_C0BE77_1788320073/subgraph' \ | | Which graph the `id` belongs to. The two graphs are completely separate. (default=`"knowledge"`) | | | Maximum traversal depth in hops. Range `1` to `10`. (default=`5`) | | | Maximum number of members returned. Range `1` to `1000`. When this clips the traversal, `is_truncated` is `true`. (default=`200`) | -| | Principals to answer as (document ACLs). The subgraph then contains only items those principals may see, filtered at every hop. Repeated (`acl=a&acl=b`) or comma-separated. Omit for no ACL scoping. | +| | Principals to answer as (document ACLs). Only items those principals may see are returned, filtered at every hop. Repeat or comma-separate values. Omit for no caller-supplied ACL filtering. For enforced connectors, the authenticated caller is used instead; see [ACL enforcement](/essentials/v2/connectors#enforce-acl). | ## How items connect diff --git a/api-reference/v2/endpoint/update-connector.mdx b/api-reference/v2/endpoint/update-connector.mdx index 2f70ca1e..23e0e981 100644 --- a/api-reference/v2/endpoint/update-connector.mdx +++ b/api-reference/v2/endpoint/update-connector.mdx @@ -4,7 +4,7 @@ description: "Change a connector's instructions, sync interval or credentials in openapi: "api-reference/v2/openapi.json PATCH /connectors/{id}" --- -Update a connector's instructions, sync interval, or credentials. Omitted fields keep their existing values. +Update a connector's instructions, sync interval, credentials, or ACL enforcement. Omitted fields keep their existing values. - HydraDB validates every field before saving. Invalid input changes nothing. - Credentials are saved before other settings. If a server error occurs afterward, only the credentials may have changed. Retrying the same request is safe. @@ -12,8 +12,17 @@ Update a connector's instructions, sync interval, or credentials. Omitted fields - `custom_instructions` applies to resources without their own instructions. Maximum 4,000 characters. Send `""` to clear it. Changes affect future syncs. See [Custom Ingestion Instructions](/essentials/v2/connectors#custom-ingestion-instructions). - `sync_interval_seconds` reschedules the next sync. Stay within the returned `min_sync_interval_seconds` and `max_sync_interval_seconds`, or send `0` for the provider default. - `credentials` reconnects the existing connector. Send the provider's complete credential set. Resources and saved sync positions are preserved. +- `enforce_acl` enables or disables caller-bound enforcement of this connector's captured ACL. Existing connectors remain `false` until an owner or writer enables it. Send `true` to enforce captured ACL on every content read, or `false` to return to opt-in request `acl` filtering. Omit it to keep the current value. The change takes effect on the next content request for already-synced documents and does not require re-ingestion. Only workspace owners and writers in a signed-in dashboard session can set it; `true` requires the provider to report `acl_supported: true`. API-key-authenticated requests cannot set it. See [ACL on synced content](/essentials/v2/connectors#enforce-acl) and [Access Control](/essentials/v2/access-control). -Only these three fields can be updated. A connector's `name`, `database`, `collection` and `provider_account_scope` cannot be changed after creation. Omit them from the request; unsupported fields can cause a `400` response. +To enable enforcement later, send this field in a PATCH request, or use **Enforce ACL** in the dashboard's connector settings: + +```json +{ + "enforce_acl": true +} +``` + +Only `custom_instructions`, `sync_interval_seconds`, `credentials`, and `enforce_acl` can be updated. A connector's `name`, `database`, `collection` and `provider_account_scope` cannot be changed after creation. Omit them from the request; unsupported fields can cause a `400` response. @@ -57,6 +66,7 @@ curl -X PATCH 'https://api.hydradb.com/connectors/{id}' \ { "connector_id": "{connector_id}", "sync_interval_seconds": 3600, + "enforce_acl": false, "next_sync_at": "2026-06-01T13:00:00Z", "min_sync_interval_seconds": 300, "max_sync_interval_seconds": 604800, @@ -70,9 +80,11 @@ curl -X PATCH 'https://api.hydradb.com/connectors/{id}' \ ## Errors -- `400`: the request includes an unsupported field, `custom_instructions` is over 4,000 characters, or `sync_interval_seconds` is outside the provider's range. Nothing is changed. +- `400`: the request includes an unsupported field, the provider does not report `acl_supported` for `enforce_acl: true`, `custom_instructions` is over 4,000 characters, or `sync_interval_seconds` is outside the provider's range. Nothing is changed. +- `403`: the request tries to set `enforce_acl` without a signed-in dashboard session for a workspace owner or writer. The ACL authorization error message is `only workspace owners and writers can change ACL enforcement`. - `404`: no connector with this id in your organization. - `422`: `credentials` does not match the provider's `credential_schema`. +- `503`: HydraDB could not read its provider catalog to check `acl_supported` for `enforce_acl: true`. Nothing is changed. Retry the request.
diff --git a/essentials/v2/access-control.mdx b/essentials/v2/access-control.mdx index 3113ee70..5e71b767 100644 --- a/essentials/v2/access-control.mdx +++ b/essentials/v2/access-control.mdx @@ -1,17 +1,17 @@ --- title: "Access Control" -description: "Restrict which people can retrieve a document, by declaring permissions yourself or letting connectors copy them from the source app." +description: "Restrict which people can retrieve a document by setting ACLs yourself or letting connectors capture them." --- By default, a query can retrieve any document in its collection. Access control changes that: a document carries an `acl`, the list of people allowed to retrieve it, and a query carries an `acl` naming who is asking. HydraDB returns only matching documents that person is allowed to see. This is how an internal search product keeps one employee's query from surfacing a private Slack channel or a restricted Drive file that belongs to someone else. -ACLs apply to knowledge sources. You set one at ingest on an [app source](/essentials/v2/app-sources), or later on an existing source ([section 4](#4-change-or-revoke-access)). [Connectors](/essentials/v2/connectors) can capture app permissions or apply your resource rules. [Memories](/essentials/v2/memories) are scoped per user with `collection` instead. +ACLs apply to knowledge sources. You set one at ingest on an [app source](/essentials/v2/app-sources), or later on an existing source ([section 4](#4-change-or-revoke-access)). [Connectors](/essentials/v2/connectors) can capture provider ACLs or apply your resource rules. [Memories](/essentials/v2/memories) are scoped per user with `collection` instead. In this guide: 1. [Set an ACL on an item and query as someone](#1-set-an-acl-on-an-item-and-query-as-someone): restrict a Slack message, then query as one person. 2. [Principals](#2-principals): the five identity forms, and what a query's `acl` list can contain. -3. [Let connectors capture permissions](#3-let-connectors-capture-permissions): which providers copy their own permissions, and how your own rule per resource fits in. +3. [Let connectors capture ACL](#3-let-connectors-capture-acl): which providers supply ACLs, and how your own resource rule fits in. 4. [Change or revoke access](#4-change-or-revoke-access): editing an ACL without re-ingesting, and when each change takes effect. 5. [Common mistakes](#common-mistakes) and [related pages](#related). @@ -183,7 +183,7 @@ Access control works alongside [metadata filters](/essentials/v2/metadata): filt `POST /context/list` takes the same `acl` field with the same meaning. The `GET /context/chunks`, `GET /context/relations`, and `GET /context/subgraph` endpoints take it as a query parameter, repeated (`acl=a&acl=b`) or comma-separated. -Access control is opt-in per request. A query without an `acl` field is not filtered and returns everything in scope. Adding ACLs to your documents changes nothing until your queries declare who is asking. +For directly ingested knowledge and content from connectors without ACL enforcement, access control is opt-in per request. A query without an `acl` field is not filtered by caller identity. For content from a connector with `enforce_acl` enabled, HydraDB enforces ACL for the authenticated caller on every read instead. See [ACL on synced content](/essentials/v2/connectors#enforce-acl) for how to enable it and how caller identities are selected. ## 2. Principals @@ -206,28 +206,43 @@ Principals are lowercased, trimmed, and deduplicated on the way in. `__public__` | You send | Behavior | |---|---| -| No `acl` field | No filtering. | +| No `acl` field | No caller-supplied ACL filtering. Connectors with ACL enforcement still apply ACL for the authenticated caller. | | `"acl": []` | Same as omitting it. On a query, an empty list does not mean nobody. | -| `"acl": ["*"]` | Same as omitting it. An explicit "search as an administrator". | -| `"acl": ["grace@acme.com"]` | Filtered to what Grace may see: her own documents, anything shared with `domain:acme.com`, public documents, and unrestricted documents. | -| `"acl": ["grace@acme.com", "group:slack:C0123"]` | Grace's view plus everything shared with that group. Group membership is never derived from an email; declare it. | -| `"acl": ["__private__"]` | Public and unrestricted documents only. | +| `"acl": ["*"]` | Same as omitting it. An explicit "search as an administrator" for content without connector enforcement. | +| `"acl": ["grace@acme.com"]` | Filtered to what Grace may see on content without connector enforcement: her own documents, anything shared with `domain:acme.com`, public documents, and unrestricted documents. | +| `"acl": ["grace@acme.com", "group:slack:C0123"]` | Grace's view plus everything shared with that group on content without connector enforcement. Group membership is never derived from an email; declare it. | +| `"acl": ["__private__"]` | Public and unrestricted documents only on content without connector enforcement. | Several emails in one list see the union of their views. An entry that is neither an email nor a recognized principal is kept as-is and matches nothing but public and unrestricted documents. A typo narrows results; it never widens them. If a caller sees less than you expect, check the principal spelling first. -## 3. Let connectors capture permissions +## 3. Let connectors capture ACL -For supported providers, HydraDB reads the source app's own permissions on every sync and applies them as ACLs, so you do not maintain a parallel permission model: +For supported providers, HydraDB captures the provider's ACL on every sync, so you do not maintain a second access model: -| Provider | What is captured | +| Provider | ACL HydraDB captures | |---|---| | **Slack** | Public channels are visible workspace-wide; private channels only to their members, resolved to member emails. | -| **Google Drive** | Per-file sharing: user, group, domain, and public grants. Permission-only changes (a share with no edit to the file) are picked up through the Drive changes feed, which content sync alone cannot see. | +| **Google Drive** | Per-file sharing: user, group, domain, and public grants. ACL-only changes are picked up through the Drive changes feed, even when the file content does not change. | | **GitHub** | Repository visibility. Private repos additionally capture the collaborator list when every collaborator has a visible public email; otherwise your resource rule governs. | -| **Confluence** | Space-level view permissions, with groups expanded to member emails, plus per-page view restrictions. | +| **Confluence** | Space-level view ACLs, with groups expanded to member emails, plus per-page view restrictions. | | **Jira** | Who holds Browse access per project, plus per-issue security levels. | -Capture is also built for GitLab, Linear, Dropbox, Zendesk, PagerDuty, Typeform, Attio, LaunchDarkly, ServiceNow, Microsoft Teams, and Outlook, and is switched on per provider. The provider catalog does not yet flag which providers capture permissions, so treat this list as the current set, and check [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) for an `acl_warning` after the first sync. For any other provider, [set your own rule on the resource](#set-your-own-rule-on-a-resource). +Capture is also built for GitLab, Linear, Dropbox, Zendesk, PagerDuty, Typeform, Attio, LaunchDarkly, ServiceNow, Microsoft Teams, and Outlook, and is switched on per provider. The [provider catalog](/api-reference/v2/endpoint/list-connector-providers) reports capture support in `acl_supported` and summarizes it in `acl_description`. Check [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) for an `acl_warning` after the first sync. For any other provider, [set your own rule on the resource](#set-your-own-rule-on-a-resource). + +By default, captured ACLs are not enforced automatically on reads. A workspace owner or writer can enable per-connector enforcement with `enforce_acl`, which is available only when the provider supports `acl_supported`. With enforcement enabled, the authenticated caller replaces a request's `acl` for content from that connector; an `acl` supplied on the request still scopes content from non-enforced connectors. See [ACL on synced content](/essentials/v2/connectors#enforce-acl) for the identity rules and the machine-readable warning returned when an `acl` is overridden. + +### Google Drive files shared with a group + +This section applies only to Google Drive. No other connector expands groups this way or reports this check. + +A Drive file shared with a Google Group carries the grant `group:google:`. To let group members read it, HydraDB reads the group's members through the Google Workspace Directory API. That needs a Drive connection made by a Workspace admin with Directory access. + +When you connect Google Drive, HydraDB checks this access once. The connection check reports the result as `google_group_membership_resolves`, and the dashboard shows it as a notice: + +- `true`: group membership can be read. Members of a group can read the files shared with that group. +- `false`: group membership cannot be read. A file shared only through a group keeps its `group:google:` grant, which no caller matches, so the file stays restricted. With ACL enforcement on, group members cannot see it. A request `acl` sees it only if it names the group. The same applies to a group too large to expand. + +To fix `false`, reconnect Google Drive as a Workspace admin with Directory access. ### Set your own rule on a resource @@ -282,13 +297,13 @@ curl -X PATCH 'https://api.hydradb.com/connectors/conn_123/resources/C_LEADERSHI -### When the provider's permissions and your rule disagree +### When the provider ACL and your rule disagree -- **A per-document permission from the provider wins over everything:** a Drive file's own sharing decides that file. +- **A document ACL from the provider wins over everything:** a Drive file's own sharing decides that file. - **A provider verdict of "this resource is public" does not override a rule you set:** restricting a public channel is a deliberate act, and enabling capture never widens it back. - **Otherwise the provider's resource-level verdict wins over your rule:** the provider is the fresher source of truth. -If HydraDB cannot read a resource's permissions from the provider, a rule you set still applies. A resource with no rule of yours is readable by everyone until the next successful read. [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) reports an `acl_warning` on the resource meanwhile, and a `page_acl_warning` when restrictions on some individual pages, for example in Confluence, could not be read. +If HydraDB cannot read a resource ACL from the provider, a rule you set still applies. A resource with no rule is readable by everyone until the next successful read. [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) reports an `acl_warning` meanwhile, and a `page_acl_warning` when some page ACLs cannot be read. ## 4. Change or revoke access @@ -366,7 +381,7 @@ Check the principal forms on both sides. `group:slack:C0123` on the document onl ## Related -- [Connectors](/essentials/v2/connectors): syncing app data, and per-resource ACL rules +- [Connectors](/essentials/v2/connectors): syncing app data, per-resource ACL rules, and caller-bound ACL enforcement - [App Sources](/essentials/v2/app-sources): the ingest shape that carries `acl` - [Query](/essentials/v2/query): the `acl` field alongside every other retrieval parameter - [Metadata and Filters](/essentials/v2/metadata): narrow results by metadata diff --git a/essentials/v2/connectors.mdx b/essentials/v2/connectors.mdx index ad8ece0e..fd011006 100644 --- a/essentials/v2/connectors.mdx +++ b/essentials/v2/connectors.mdx @@ -14,7 +14,7 @@ In this guide: 3. [Change a connector after setup](#3-change-a-connector-after-setup): which call changes what. 4. [Custom ingestion instructions](#4-custom-ingestion-instructions): tell a connector how to read its documents. 5. [Filter on what HydraDB writes](#5-filter-on-what-hydradb-writes): the metadata every synced object carries. -6. [Permissions on synced content](#6-permissions-on-synced-content): the source app's permissions, and your own rules. +6. [ACL on synced content](#6-acl-on-synced-content): captured ACL, your own rules, and optional ACL enforcement. 7. [Connect several accounts of one provider](#7-connect-several-accounts-of-one-provider). 8. [Common mistakes](#common-mistakes) and [related pages](#related). @@ -277,11 +277,12 @@ curl -X POST 'https://api.hydradb.com/connectors/{id}/configure' \ | `credentials` | create | The provider's credentials, in the shape of its `credential_schema`. Credentials that do not match the schema are rejected with `422`. | | `custom_instructions` | create, configure | Guidance for how synced documents are interpreted, for the whole connector or for one resource. See [section 4](#4-custom-ingestion-instructions). | | `sync_interval_seconds` | create | How often scheduled syncs run. Omit it for the provider default, one hour for most providers. | +| `enforce_acl` | create, update | Whether reads from this connector always enforce ACL for the authenticated caller. Optional and `false` by default. Only workspace owners and writers can set it, and enabling it requires a provider with `acl_supported`. See [ACL enforcement](#enforce-acl). | | `lookback_days` | configure | How much history the first sync fetches (default `30`). After that, each sync fetches only what is new. Above 30, some providers fetch the older history in the background and the response reports `backfill: true`. | | `resource_id`, `resource_type`, `name` | configure | The `id`, `resource_type`, and `name` of an entry from discover. Send all three every time you list a resource. | | `metadata` | configure | Fields merged into the database metadata of every object from this resource. Declare the ones you filter on in your database's [metadata schema](/essentials/v2/metadata). | | `additional_metadata` | configure | Free-form fields merged into the additional metadata of every object from this resource. No schema needed. | -| `acl` | configure | Who can read this resource's objects. Omitted means unrestricted. See [permissions](#6-permissions-on-synced-content). | +| `acl` | configure | Who can read this resource's objects. Omitted means unrestricted. See [ACL](#6-acl-on-synced-content). | @@ -507,14 +508,43 @@ Filter on `resource_id` the same way to scope to one channel, repository, or tab To see which provider-native fields a connector writes, call [`GET /connectors/providers?id={provider}`](/api-reference/v2/endpoint/list-connector-providers). Its `filterable_fields` each carry the `filter_key` to use in `metadata_filters`. Its `searchable_fields` are combined into each document's searchable text, so you search them together, not one at a time. - - -## 6. Permissions on synced content +## 6. ACL on synced content -For supported providers, HydraDB reads the source app's permissions on every sync and applies them as document access rules, so a query made on someone's behalf cannot surface a private channel, a restricted Drive file, or a repository they cannot see. Slack, Google Drive, GitHub, Confluence, and Jira are among the supported providers; [Access Control](/essentials/v2/access-control) lists what is captured for each. +For supported providers, HydraDB captures ACL from the provider on every sync. Slack, Google Drive, GitHub, Confluence, and Jira are among the supported providers; [Access Control](/essentials/v2/access-control) lists what is captured for each. Capture and enforcement are separate: the connector's `enforce_acl` setting controls ACL enforcement on reads. -Send the caller's identity as `acl` on every query. A query without it ignores document permissions. +By default, `enforce_acl` is `false`. Omitting it on create leaves the new connector unenforced, and existing connectors remain unenforced until an owner or writer enables the setting. In this mode, access control remains opt-in on each request: send the caller's identity in `acl` to filter content, and a request without `acl` is not filtered by caller identity. This remains true for content from connectors whose enforcement is disabled, even when the same request also reads enforced connector content. MCP OAuth does not enforce caller identity for a non-enforced connector; send `acl` when that request needs caller filtering. + +### Enforce ACL + +Set `enforce_acl` to `true` to make reads from this connector use the authenticated caller's identity against the ACL HydraDB captured. The provider must report `acl_supported: true` in [List Connector Providers](/api-reference/v2/endpoint/list-connector-providers). The setting is optional on [`POST /connectors`](/api-reference/v2/endpoint/create-connector), defaults to `false`, and can be enabled or changed later with [`PATCH /connectors/{id}`](/api-reference/v2/endpoint/update-connector). In the dashboard, an owner or writer can use the **Enforce ACL** control in the connector settings. A change takes effect on the next content request for already-synced documents and does not require re-ingestion. + +HydraDB decides which providers report `acl_supported`. If it stops reporting it for a provider, that provider's connectors stop enforcing within about a minute and keep their stored `enforce_acl` value. Enforcement resumes when support returns. + +Only workspace owners and writers can set or change this setting. Readers can view the current setting but cannot change it. The API requires an owner or writer in a signed-in dashboard session; API-key-authenticated requests can create a connector with the default `false` value, but cannot choose or change the setting. + +When enforcement is enabled, HydraDB selects the identity from authentication rather than trusting a caller-supplied `acl`: + +- **API key or pasted-key MCP:** the email of the person who created the key. +- **MCP OAuth:** the signed-in user. +- **Dashboard:** the signed-in user. + +If a content-read request includes `acl`, HydraDB ignores it for content from connectors with enforcement enabled. The `acl` still scopes content from non-enforced connectors. A successful response includes exactly one machine-readable warning for the override in `meta.warnings`: + +```json +{ + "meta": { + "warnings": [ + { + "code": "acl_ignored_by_connector_enforcement", + "message": "This connector enforces ACL, so the request's acl was ignored." + } + ] + } +} +``` + +An API key stays tied to its creator. When a member is removed from the workspace, or leaves it, every API key they created in that workspace is revoked. Re-inviting them does not restore those keys; they create new ones. Another member must use a key created under their own account. Your own rule per resource goes in `acl` at configure time, or afterwards on its own: @@ -544,9 +574,9 @@ curl -X PATCH 'https://api.hydradb.com/connectors/{id}/resources/C_LEADERSHIP' \ -The change applies to already-synced documents on the next query, with no re-sync. Where HydraDB reads the provider's permissions, a per-document permission from the provider wins at the next sync; [Access Control](/essentials/v2/access-control) explains the full precedence. +The change applies to already-synced documents on the next query, with no re-sync. A document ACL from the provider wins at the next sync; [Access Control](/essentials/v2/access-control) explains the full precedence. -If HydraDB cannot read a resource's permissions from the provider, [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) reports an `acl_warning` on it until the next successful read. Meanwhile your own rule on the resource still applies; a resource with no rule is readable by everyone. +If HydraDB cannot read a resource ACL from the provider, [Get Connector Status](/api-reference/v2/endpoint/get-connector-status) reports an `acl_warning` until the next successful read. Your own resource rule still applies; a resource with no rule is readable by everyone. diff --git a/essentials/v2/query.mdx b/essentials/v2/query.mdx index d1be2b7a..a7f9aad6 100644 --- a/essentials/v2/query.mdx +++ b/essentials/v2/query.mdx @@ -223,7 +223,7 @@ Three fields bring in context beyond the passages that matched. All three are on ## 6. Query on behalf of a user -Send the caller's email in `acl`, such as `"acl": ["grace@acme.com"]`, and results are limited to documents that person may retrieve, plus public and unrestricted ones. Without `acl` the query is not filtered, so derive it from your signed-in session, never from the client. [Access Control](/essentials/v2/access-control) covers setting ACLs on documents and the principal forms. +For directly ingested knowledge and content from connectors without ACL enforcement, send the caller's email in `acl`, such as `"acl": ["grace@acme.com"]`, to filter results to documents that person may retrieve, plus public and unrestricted ones. Without `acl`, that content is not filtered by caller identity. For connectors with `enforce_acl` enabled, HydraDB uses the authenticated caller instead and ignores a request `acl` for that connector's content. API keys use the key creator's email; MCP OAuth and dashboard requests use the signed-in user. If you send `acl` while enforcement is enabled, a successful response includes one `meta.warnings` entry with code `acl_ignored_by_connector_enforcement`. See [Access Control](/essentials/v2/access-control) and [ACL on synced content](/essentials/v2/connectors#enforce-acl) for more. ## Common mistakes