Skip to content

Commit 431355a

Browse files
authored
feat(coda): add permission-aware knowledge connector (#7915)
* feat(coda): add permission-aware knowledge connector * fix(coda): refine setup docs and verification coverage * fix(coda): clarify workspace access in setup guide
1 parent 452b35c commit 431355a

27 files changed

Lines changed: 2378 additions & 7 deletions

apps/docs/content/docs/knowledgebase/connectors.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ Connectors continuously sync documents from external services into your knowledg
1616

1717
<Image src="/static/connectors/connectors-sources.png" alt="The current Connect Source picker showing searchable connectors including Airtable, Asana, Ashby, Azure DevOps, Bitbucket, Box, and ClickUp" width={750} height={680} className="mx-auto h-auto w-full max-w-md" />
1818

19-
Sim ships with 64 built-in connectors:
19+
Sim ships with 66 built-in connectors:
2020

2121
| Category | Connectors |
2222
|----------|-----------|
23-
| **Productivity** | Notion, Confluence, Asana, Linear, Jira, Jira Service Management, Monday, Trello, ClickUp, Google Calendar, Google Sheets, Google Forms, Microsoft Excel, Typeform |
23+
| **Productivity** | Coda, Notion, Confluence, Asana, Linear, Jira, Jira Service Management, Monday, Trello, ClickUp, Google Calendar, Google Sheets, Google Forms, Microsoft Excel, Typeform |
2424
| **Cloud Storage** | Google Drive, Dropbox, OneDrive, SharePoint, Box, Amazon S3, SFTP |
2525
| **Documents** | Google Docs, Google Slides, Mintlify, WordPress, Webflow, DocuSign |
2626
| **Development** | GitHub, GitLab, Bitbucket, Azure DevOps, Sentry |
@@ -31,7 +31,7 @@ Sim ships with 64 built-in connectors:
3131
| **Incident Management** | incident.io, Rootly, PagerDuty |
3232
| **Data** | Airtable, Databricks |
3333
| **Note-taking** | Obsidian |
34-
| **Meetings** | Zoom, Google Meet, Gong, Grain, Granola, Fathom, Fireflies |
34+
| **Meetings** | Zoom, Google Meet, Gong, Grain, Granola, Fathom, Fireflies, Circleback |
3535
| **Recruiting** | Greenhouse, Ashby |
3636
| **HR** | Workday Help |
3737
| **Compliance** | Google Vault |
@@ -52,6 +52,7 @@ Other connectors use **API keys** or **personal access tokens** instead. The set
5252
| Connector | Where to get the key |
5353
|-----------|---------------------|
5454
| **Obsidian** | Install the [Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api) plugin, then copy the key from its settings |
55+
| **Coda** | API token from Account settings → API connections, saved as a Coda service account credential |
5556
| **Fireflies** | Generate from the Integrations page in your Fireflies account |
5657
| **Typeform** | Personal access token from your Typeform account settings |
5758
| **Azure DevOps** | Personal access token with Wiki (Read), Work Items (Read), and Code (Read) scopes |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
title: Coda
3+
description: Connect Coda documents while preserving each teammate's access
4+
---
5+
6+
import { Step, Steps } from 'fumadocs-ui/components/steps'
7+
import { Image } from '@/components/ui/image'
8+
9+
An organization admin connects Coda once. Teammates search using their verified Sim email address; they do not need a personal Coda connection. Their email must match the address Coda reports.
10+
11+
## Connect Coda
12+
13+
<Steps>
14+
<Step>
15+
16+
### Create an API token
17+
18+
Sign in to Coda with a dedicated integration account that can read the documents and their sharing permissions. Open **Account settings → API connections → Generate API token**, name the token, and copy it. Coda's current app may be labeled **Superhuman Docs**.
19+
20+
Use a standard REST API token. Do not select the MCP restriction.
21+
22+
<Image className="mx-auto h-auto w-full max-w-2xl" src="/static/search/coda-api-token.jpg" alt="Coda API connections with the Generate new token dialog" width={1280} height={720} />
23+
24+
</Step>
25+
<Step>
26+
27+
### Add the source
28+
29+
In Sim, open **Settings → Sources → Add source → Coda**. Add or select a Coda service account credential and enter the token when prompted.
30+
31+
| Field | What to enter |
32+
|---|---|
33+
| Documents | Select documents, or switch to **Document IDs** and enter up to 100 IDs separated by commas. For `https://coda.io/d/_dAbCDeFGH`, the ID is `AbCDeFGH`. |
34+
| Enterprise organization ID | Leave blank for ordinary access. For organization-wide indexing, enter the ID from Coda's [List organizations API](https://coda.io/developers/apis/admin/v1#operation/listOrganizations) and use an Enterprise organization administrator's token. Workspace admin access alone is insufficient. |
35+
36+
The picker shows accessible documents the token owner has opened. Use IDs for other accessible documents. With no selection, Sim discovers the token owner's opened documents, or the organization's documents in Enterprise mode. Use explicit IDs when deleted documents must reliably be removed from the index; discovery listings alone are not authoritative for deletion.
37+
38+
</Step>
39+
<Step>
40+
41+
### Sync and verify
42+
43+
Select **Connect & Sync**. Open the source's **Documents** and **Sync history** to check indexing. Content and permission changes appear after synchronization.
44+
45+
To rotate the token, add the replacement credential, update the source, and verify a sync before revoking the old token.
46+
47+
</Step>
48+
</Steps>
49+
50+
## Content and access
51+
52+
Ordinary connections index visible canvas text and base-table rows. Enterprise connections index the Admin API's plaintext page exports; tables are included only as represented in those exports. Attachments, comments, images, and remote embeds are not extracted.
53+
54+
| Permission | Ordinary connection | Enterprise connection |
55+
|---|---|---|
56+
| Owner or direct email share | Matching verified Sim email | Matching active organization user |
57+
| Group, workspace, or domain share | Not expanded | Resolved through the Admin API |
58+
| Anyone with the link | Does not grant Search access | Does not grant Search access |
59+
60+
Enterprise mode excludes deactivated and deleted users. Direct-share guests absent from the organization directory are excluded; guests explicitly listed in a group or workspace may receive that membership's access. Ordinary connections cannot check Coda organization deactivation, so manage departing users' Sim membership too.
61+
62+
Sim refreshes permissions even when content is unchanged. Unverified permissions never become broad access, and Sim admin status does not override Coda sharing. For setup failures or missing results, check the token's access, Enterprise admin role if applicable, the teammate's verified email, and **Sync history**.
63+
64+
In a regular knowledge base, **Workspace access** intentionally shares all indexed content with that Sim workspace and does not use the Enterprise organization ID. The permissions above apply to organization Search and **Admin access** sources.
65+
66+
See Coda's [public API](https://coda.io/developers/apis/v1) and [Enterprise Admin API](https://coda.io/developers/apis/admin/v1).

apps/docs/content/docs/search/connect-your-account.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ For a source configured inside a workspace, join that workspace and connect thro
6060
| GitHub App installation | Connect GitHub once for this Sim organization. The App handles indexing; your account establishes which repositories you may search. |
6161
| Confluence service account | Connect Confluence to verify your identity; the service account handles the crawl. |
6262
| Google Workspace service account (Gmail, Calendar, Drive) | No personal connection is needed for that source. Your verified Sim email identifies your mailbox and calendar view, or is matched to Drive permissions. |
63+
| [Coda](/search/coda) | No personal connection is needed. Your verified Sim email must match a supported Coda sharing grant. |
6364
| GitLab | No personal connection is needed. For an administrator token, your verified Sim email must match your confirmed primary GitLab email. For a non-admin token, it must match the uploaded user mapping and have a project grant. |
6465

6566
Gmail, Calendar, and Drive are separate Search sources. Connecting one Google service does not connect all of them. Central Google and GitLab sources remain searchable without a personal account connection.

apps/docs/content/docs/search/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"pages": [
44
"connect-your-account",
55
"mcp",
6+
"coda",
67
"confluence",
78
"github",
89
"gitlab",
47.7 KB
Loading

apps/sim/connectors/coda/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Coda connector decisions and verification
2+
3+
## Precedent and authentication
4+
5+
| Reference | Verified behavior | What it establishes for Sim |
6+
| --- | --- | --- |
7+
| [Glean Coda search](https://www.glean.com/connectors/coda) | Native document/page indexing with query-time source permissions. | The same access-control objective. The public page does not specify the crawler's credentials, endpoints, directory expansion, or deletion strategy; exact implementation parity is not established. |
8+
| [Glean Coda agent service credential](https://docs.glean.com/administration/agent-identity/coda) | A dedicated Coda user and an MCP-restricted token for agent tools. | Dedicated identity and credential rotation are useful operational precedent. This is a separate MCP integration, not the REST indexing setup; its MCP token restriction must not be copied into Sim's connector instructions. |
9+
| [Onyx Coda connector](https://github.com/onyx-dot-app/onyx/blob/5fe6573c3c155e1a75b51de32d4988ee6c82164e/backend/onyx/connectors/coda/connector.py) | A bearer token, optional workspace scope, page content, and table rows through the public REST API. | Direct precedent for ordinary content ingestion. Sim uses bounded deferred hydration rather than collecting the entire source before indexing. |
10+
11+
Onyx's [test setup](https://github.com/onyx-dot-app/onyx/blob/5fe6573c3c155e1a75b51de32d4988ee6c82164e/backend/tests/daily/connectors/coda/README.md) also uses an API token generated in account settings. Its reviewed Coda implementation has no source ACL or directory calls, and its [permission-sync registry](https://github.com/onyx-dot-app/onyx/blob/5fe6573c3c155e1a75b51de32d4988ee6c82164e/backend/ee/onyx/external_permissions/sync_params.py) has no Coda entry. It therefore does not establish Enterprise permission parity. Coda's own API documentation is the authority for Sim's Enterprise path.
12+
13+
This connector reuses Sim's existing `coda-service-account` token credential and its selector. The metadata's `oauth` discriminator means “use a stored credential” in the shared connector framework. The actual credential is an API token, with no OAuth authorization or refresh flow. Personal credential-group enrollment is not exposed because that flow currently requires OAuth. Admin indexing matches provider-reported grants to verified Sim emails instead.
14+
15+
## API contract audit
16+
17+
All connector requests are GET requests authenticated with `Authorization: Bearer`. The following operation IDs were checked against the OpenAPI specifications embedded in Coda's [public reference](https://coda.io/developers/apis/v1) and [Admin reference](https://coda.io/developers/apis/admin/v1), including query parameters, response fields, pagination, and allowed formats. None of these operations is deprecated.
18+
19+
| API | Operations used |
20+
| --- | --- |
21+
| Public identity and discovery | `whoami`, `listDocs`, `getDoc` |
22+
| Public content | `listPages`, `getPage`, `listPageContent`, `listTables`, `getTable`, `listColumns`, `listRows` |
23+
| Public permissions | `getPermissions` |
24+
| Admin discovery | `getOrganization`, `listDocs` with `docIds` or `docAvailabilityState` and `fetchPermissionsMode=none` |
25+
| Admin content and permissions | `listPagesV2`, `getPageV2` with `outputFormat=LossyPlainText`, `listDocPermissionsV2` |
26+
| Admin directory | `listOrgUsers`, `listGroups`, `listGroupMembers`, `listWorkspaces`, `listWorkspaceUsers` |
27+
28+
The Admin API requires an [Enterprise organization administrator](https://help.superhuman.com/hc/en-us/articles/46210125237901-Intro-to-Superhuman-Docs-Admin-API). Ordinary workspace admin access does not satisfy that requirement. All network calls use the shared secure fetch/retry path and fixed API origins; the connector uses no browser session, private endpoint, credential impersonation, or provider permission mutation. Synthetic directory groups are Sim's internal representation of documented grants, not invented Coda API objects. Size and crawl limits below are Sim safeguards, not claimed Coda service limits.
29+
30+
## Access tradeoffs
31+
32+
- Without an organization ID, only ownership and direct email grants establish individual access. Workspace, domain, group, and unknown principals are not expanded. Link possession never grants Search access. The public API does not provide organization account status; this mode cannot independently detect organization-level deactivation while a direct grant remains. Sim membership lifecycle must also be managed.
33+
- With an Enterprise organization ID, the Admin API supplies organization discovery and directory membership. Direct users and domains use synthetic groups so deactivated/deleted organization users cannot resolve grants. External direct-share guests absent from the organization directory are conservatively excluded. Explicit group/workspace guests remain eligible unless the directory reports them inactive.
34+
- Opaque group/workspace IDs are hex encoded before shared case-folding. Tenant IDs remain source-exact. This avoids merging case-sensitive Coda IDs.
35+
- Every listed document's complete ACL is read on every sync, independently of content hashes. Partial/failed ACLs are omitted so the shared engine fails closed. Directory snapshots are collected under the shared directory lease and never reported complete after a truncated response.
36+
- Permission freshness, query authorization, and directory persistence remain in the existing shared engines. Workspace access mode deliberately grants workspace-wide access and is separate from organization Search.
37+
38+
## Content and pagination tradeoffs
39+
40+
The [public API](https://coda.io/developers/apis/v1) exposes canvas plaintext and base-table rows; the [Admin API](https://coda.io/developers/apis/admin/v1) exposes `LossyPlainText` page exports. Admin exports are not equivalent to independently fetching every table. Hidden/embed/synced pages are excluded on the public path; the Admin API does not expose those public page-type flags. Neither path extracts arbitrary attachments or remote embeds.
41+
42+
Admin document reads use the current workspace-qualified endpoints, resolving the workspace with the documented organization `docIds` filter. Metadata queries request `fetchPermissionsMode=none`; permissions come only from the complete paginated ACL endpoint.
43+
44+
Listing defers content and retains only the current parent document metadata in the per-run context, avoiding one redundant parent fetch per child. Resource content and permissions are still read from the provider; ACLs never use that metadata cache. Discovery fetches ten parent documents at a time. Compound cursors persist that bounded queue, the current document, child phase, and provider token for durable replay, under the shared 512 KiB cursor limit. Only `pageToken` is sent on continuation requests, as Coda requires. No response URL or redirect receives the credential. Responses, text, list lengths, ACLs, directory entries, and pagination loops are bounded and fail rather than silently truncating. Responses are limited to 4 MiB, indexed text to 12 MiB, and hydration concurrency to two. The stubs advertise the text bound to the shared byte-budget scheduler. Directory entries are limited to 100,000, email lengths to 254, and document permissions to 5,000. Domain memberships are indexed once instead of scanning every user for every domain.
45+
46+
Hashes use the parent document revision because table-list references do not guarantee a row-sensitive timestamp. A document edit invalidates all its children, trading extra reads for correctness. Explicit full resyncs also refresh rendered dependencies. When the Admin API omits its optional revision timestamp, the shared durable sync-generation ID forces one refresh per crawl, following the Slack/GitLab pattern. It remains stable across retries within that generation.
47+
48+
Discovery order is mutable and the public list omits never-opened documents. Discovery is therefore non-authoritative for deletion reconciliation. Explicit document IDs provide authoritative scope; confirmed 404/410 documents reconcile, while 403s remain errors. Up to 100 explicit IDs are supported per source. Setup probes one document and one ACL page to keep request latency bounded; sync checks the remaining scope.
49+
50+
## Reproducible checks
51+
52+
From `apps/sim`:
53+
54+
```sh
55+
bunx vitest run connectors/coda/coda.test.ts connectors/coda/permissions.test.ts lib/selectors/server/providers/coda.test.ts lib/credentials/token-service-accounts/validators/coda.test.ts lib/selectors/manifest.test.ts
56+
CODA_CONNECTOR_LIVE_TOKEN_FILE=/path/to/token bunx vitest run connectors/coda/coda.live.test.ts
57+
```
58+
59+
The provider test creates and deletes its own document. `CODA_CONNECTOR_LIVE_ORGANIZATION_ID` enables additional Enterprise export/ACL/directory checks against that fixture when the token belongs to an Enterprise organization. Set `CODA_CONNECTOR_LIVE_FIXTURE_FILE=/path/to/fixture.json` to retain it for the application test, then from the repository root:
60+
61+
```sh
62+
CODA_CONNECTOR_LIVE_TOKEN_FILE=/path/to/token \
63+
CODA_CONNECTOR_LIVE_FIXTURE_FILE=/path/to/fixture.json \
64+
CODA_CONNECTOR_LIVE_SECOND_EMAIL=second-user@example.com \
65+
bun scripts/test-knowledge-acls.ts coda-live.integration.ts
66+
```
67+
68+
The application test uses disposable PostgreSQL/Redis, real credentials, Coda, source creation, ingestion, storage, and authorization. Set `CODA_CONNECTOR_LIVE_SCOPE=organization` to exercise organization credential creation and organization Search instead of a workspace knowledge base. Only embeddings are deterministic substitutes. It changes only the retained fixture's share, with notifications suppressed, and removes that share afterward. `CODA_CONNECTOR_LIVE_ALLOW_SHARING=false` explicitly skips the live grant/revoke cases when source policy prohibits sharing. `CODA_CONNECTOR_LIVE_UI_FIXTURE_FILE` optionally retains isolated rows and writes a temporary signed session for browser verification; use only with the runner's disposable-database retention options and remove both afterward.
69+
70+
## Verification coverage
71+
72+
- Focused connector, selector, credential, navigation, configuration, and integration-availability tests passed. App and deployment-config type checks, Biome, and the strict API-contract audit passed.
73+
- Live provider tests cover page/table hydration, page edits, and row edits. Coda's real initialization `409` led to an explicit retry path.
74+
- Real application setup and ingestion passed in both workspace and organization scope (the live sharing cases require a workspace that allows the intended cross-domain share). Organization scope creates the token credential through the authorized application use case. Owner search/chunks were allowed; another verified user, unverified owner, and workspace API key were denied. Embeddings were substituted, so external embedding-provider behavior was not tested.
75+
- Sim's browser showed the Coda source Active, both indexed documents, the live document picker, the saved organization credential, and preserved selection across connection/input modes. Browser checks caught and fixed missing Search URL registration, scope loss when switching modes, and deployment metadata rejecting Coda's token credential. Coda's official browser app showed the fixture content.
76+
- Live sharing to the requested second account was rejected by the source policy: “Cross domain sharing is prohibited.” Grant/revoke tests remain unverified live, with unit coverage for ACL changes and incomplete responses.
77+
- The available test account returned no Enterprise organizations; the official UI offers sales-assisted Enterprise setup rather than a self-service trial. Admin API behavior is checked against published schemas and mocked responses; organization-wide crawling, deactivation, and group/workspace/domain permission parity still require an Enterprise tenant test before being considered release-verified.
78+
79+
These remaining provider prerequisites prevent claiming complete live verification.

0 commit comments

Comments
 (0)