> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Connectors

> Enabling the Microsoft 365 (Outlook, OneDrive, SharePoint, Teams) and other OAuth connectors on a customer-deployed Nebula Enterprise install

Nebula ships OAuth connectors that sync content from external sources into a collection. The connector **runtime** is built into every Enterprise release; this page covers the **operator setup** — registering the OAuth app and wiring its credentials into your deployment. Once that's done, your users connect their own accounts through the [Connectors API](/guides/connectors).

The four Microsoft 365 connectors — **Outlook**, **OneDrive**, **SharePoint**, and **Teams** — share one Entra ID (Azure AD) app registration. Google connectors — **Gmail** and **Google Drive** — share one Google OAuth app per workspace.

<Note>
  Connectors are **off by default** until `NEBULA_CONNECTOR_TOKEN_KEY` is configured. Google and Microsoft 365 require workspace OAuth apps. Slack and Notion still require their deployment-level client ID and secret before those providers are listed.
</Note>

## How it fits together

| Piece                                              | Who provides it                       | Where it goes                                             |
| -------------------------------------------------- | ------------------------------------- | --------------------------------------------------------- |
| Entra app registration (client ID, secret, tenant) | You, in the Azure portal              | Deployment secrets (below)                                |
| OAuth callback base URL                            | Your deployment's public API origin   | `NEBULA_CONNECTOR_API_BASE_URL` / `connectors.apiBaseUrl` |
| Token-signing HMAC                                 | Auto-generated (Compose) or you (K8s) | `NEBULA_CONNECTOR_TOKEN_KEY`                              |
| End-user account authorization                     | Each of your users                    | Connectors API / app UI                                   |

The token-signing key (`NEBULA_CONNECTOR_TOKEN_KEY`) signs every connector's OAuth state and stored tokens. It is required for **any** connector and should be treated like `NEBULA_SECRET_KEY` — long-lived, rotating it invalidates all in-flight authorizations.

## Step 1 — Register the Entra app

<Steps>
  <Step title="Create the registration">
    In the Azure portal: **Entra ID → App registrations → New registration**. Register a single-tenant app (or multi-tenant if you serve external organizations). Copy the **Application (client) ID** and the **Directory (tenant) ID**.
  </Step>

  <Step title="Create a client secret">
    **Certificates & secrets → New client secret**. Copy the secret **Value** (not the secret ID — the value is shown only once).
  </Step>

  <Step title="Add the redirect URIs">
    **Authentication → Add a platform → Web**, then add one redirect URI per connector, substituting your deployment's public API origin:

    ```
    https://nebula.example.com/v1/connectors/outlook/callback
    https://nebula.example.com/v1/connectors/onedrive/callback
    https://nebula.example.com/v1/connectors/sharepoint/callback
    https://nebula.example.com/v1/connectors/teams/callback
    ```

    The origin must match `NEBULA_CONNECTOR_API_BASE_URL` (Compose) / `connectors.apiBaseUrl` (Helm) exactly. Entra rejects non-HTTPS and non-public redirect URIs (except `localhost`).
  </Step>

  <Step title="Grant Graph permissions">
    **API permissions → Microsoft Graph → Delegated permissions**, add the scopes for the connectors you intend to offer, then **Grant admin consent**:

    | Connector  | Delegated scopes                                                                                          |
    | ---------- | --------------------------------------------------------------------------------------------------------- |
    | Outlook    | `Mail.Read`                                                                                               |
    | OneDrive   | `Files.Read`                                                                                              |
    | SharePoint | `Sites.Read.All`, `Files.Read.All`                                                                        |
    | Teams      | `Team.ReadBasic.All`, `Channel.ReadBasic.All`, `ChannelMessage.Read.All`, `Chat.Read`, `ChatMessage.Read` |
    | All        | `User.Read`, `offline_access`                                                                             |
  </Step>
</Steps>

## Step 2 — Wire the credentials into your deployment

<CodeGroup>
  ```bash Docker Compose (.env.enterprise) theme={null}
  # generate-secrets.sh already populated NEBULA_CONNECTOR_TOKEN_KEY.
  # Set your public API origin and, optionally, constrain the Entra tenant:
  NEBULA_CONNECTOR_API_BASE_URL=https://nebula.example.com
  # Tenant ID or a verified domain for single-tenant apps.
  # Omit to default to "organizations" (any work/school tenant).
  NEBULA_M365_TENANT=<directory-tenant-id>
  ```

  ```yaml Kubernetes (Helm values) theme={null}
  # Non-secret connector config. apiBaseUrl defaults to https://<ingress.host>
  # when left empty.
  connectors:
    apiBaseUrl: ""                      # override only if it differs from ingress.host
    m365:
      tenant: <directory-tenant-id>     # omit/empty -> "organizations"

  # Secrets ride the app-secrets Secret. With secrets.backend: raw:
  secrets:
    values:
      NEBULA_CONNECTOR_TOKEN_KEY: <long-random-string>
  ```
</CodeGroup>

With `secrets.backend: eso-aws` or `eso-vault`, put `NEBULA_CONNECTOR_TOKEN_KEY` in your secret store under the path the `ExternalSecret` syncs. The chart `envFrom`-mounts the synced Secret onto **all** first-party Nebula workloads (api, worker, graph-engine, compactor, and the migration Job), so every key lands on every Nebula pod. Only the api (OAuth connect) and worker (sync) actually read the connector keys; the other workloads ignore them. Keep this synced path scoped to Nebula's own secrets — don't co-locate unrelated application secrets under it.

<Warning>
  `NEBULA_CONNECTOR_API_BASE_URL` must be the **externally reachable** origin your users hit, because the browser is redirected there after authorizing. The in-image default (`http://localhost:7272`) only works for a single-host local trial. On Helm it auto-derives from `ingress.host`; on Compose you must set it explicitly.
</Warning>

Apply the change (`bootstrap.sh` re-run for Compose, `helm upgrade` for Kubernetes) and the connectors are live. Both the API pod (OAuth connect flow) and the worker pod (background sync + webhooks) read the same configuration.

## Step 3 — Connect accounts

Connector authorization is per-user, while Google and Microsoft 365 OAuth apps are saved per workspace. A workspace owner or admin enters the app once; later users authorize their own account against that workspace app. Workspace apps provide isolated quotas and direct provider review control. The two-phase providers (Google Drive, OneDrive, SharePoint, Teams) prompt for a folder / library / channel selection after authorization; Gmail and Outlook sync immediately.

Custom Microsoft 365 apps should be multi-tenant, or they must belong to the tenant configured by `NEBULA_M365_TENANT`.

## Other connectors

Notion and Slack follow the same base shape — register an OAuth app with that provider's console, add the matching redirect URI (`<base>/v1/connectors/<provider>/callback`), and set the provider's client ID / secret pair (Compose) or the equivalent `secrets.values` keys (Helm).

The workspace Google OAuth app must register the connector callback for the provider being connected:

* `<base>/v1/connectors/gmail/callback`
* `<base>/v1/connectors/google_drive/callback`

Two providers need provider-specific extras beyond the OAuth client pair:

* **Slack** also requires a signing secret (`NEBULA_SLACK_SIGNING_SECRET`) and an Events API subscription to verify and receive inbound events.
* **Gmail** real-time sync requires Google Pub/Sub push. For a workspace Google OAuth app, create a Pub/Sub topic in the OAuth app's Google Cloud project, grant `gmail-api-push@system.gserviceaccount.com` Publisher on the topic, create a push subscription to the workspace Gmail webhook URL shown in Nebula, enable OIDC auth with your push service account, then save the topic and service-account email on the workspace OAuth app. If the push subscription uses a custom OIDC audience, save that audience too; otherwise Nebula verifies the webhook URL as the audience. Without those workspace settings, Gmail uses polling.
* **Google Drive** real-time sync for workspace OAuth apps requires the Drive webhook callback domain to be verified in the customer's Google Cloud project. SaaS deployments should leave workspace Drive watch disabled so Drive uses polling; self-hosted deployments that own the callback domain can enable `NEBULA_GOOGLE_DRIVE_WORKSPACE_OAUTH_WATCH_ENABLED=true`.

For Compose, the complete per-provider variable list lives in `.env.enterprise.example`. For Helm, add Slack and Notion provider secrets under `values.yaml` `secrets.values`; any key under `secrets.values` becomes an env var on the pods.
