Skip to content

Commit ba6e2b4

Browse files
feat: add per-connection enforcePermissions toggle and promote PERMISSION_SYNC_ENABLED (#991)
* feat(worker): add per-connection enforcePermissions toggle and promote PERMISSION_SYNC_ENABLED - Add `enforcePermissions` boolean to all connection config schemas (github, gitlab, bitbucket, gitea, gerrit, azuredevops, genericGitHost). Defaults to the value of `PERMISSION_SYNC_ENABLED` when not set. - Add `enforcePermissions` column to the `Connection` DB model (migration: 20260310050300). - Sync `enforcePermissions` from config in `configManager.ts` on create/update. - Filter repo permission syncer to only queue repos with at least one connection with `enforcePermissions: true`. - Update `getRepoPermissionFilterForUser` in `prisma.ts` to exempt repos where no connection has `enforcePermissions: true`. - Deprecate `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` in favour of `PERMISSION_SYNC_ENABLED` (backwards-compatible via Zod transform fallback). - Remove `isPermissionSyncEnabled()` helper; replace all call sites with `env.PERMISSION_SYNC_ENABLED === 'true'`. - Add unit tests for `PERMISSION_SYNC_ENABLED` env var behaviour in `packages/shared/src/env.server.test.ts`. - Extract `createEnv` options as named export to preserve JSDoc (`@deprecated`) in emitted `.d.ts` (see microsoft/TypeScript#62309). - Update permission-syncing and environment-variables docs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update CHANGELOG for #991 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add repoDrivenPermissionSyncIntervalMs and userDrivenPermissionSyncIntervalMs config settings - Add `repoDrivenPermissionSyncIntervalMs` and `userDrivenPermissionSyncIntervalMs` to the config schema, deprecating the `experiment_` prefixed variants (still respected as fallbacks in getConfigSettings). - Update repoPermissionSyncer and accountPermissionSyncer to use the new setting names. - Add tests for getConfigSettings fallback behaviour. - Update docs and CHANGELOG. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feedback --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ab1ff6d commit ba6e2b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+720
-74
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- [EE] Added multi-owner support with promote/demote actions. [#988](https://github.com/sourcebot-dev/sourcebot/pull/988)
1212
- [EE] Added `PERMISSION_SYNC_REPO_DRIVEN_ENABLED` environment variable to enable/disable repo-driven permission syncing. [#989](https://github.com/sourcebot-dev/sourcebot/pull/989)
13+
- [EE] Added `enforcePermissions` per-connection flag to control whether repository permissions are enforced for a given connection. Defaults to the value of `PERMISSION_SYNC_ENABLED`. [#991](https://github.com/sourcebot-dev/sourcebot/pull/991)
14+
- [EE] Added `repoDrivenPermissionSyncIntervalMs` and `userDrivenPermissionSyncIntervalMs` config settings, deprecating the `experiment_` prefixed variants (still respected as fallbacks). [#991](https://github.com/sourcebot-dev/sourcebot/pull/991)
15+
16+
### Changed
17+
- [EE] Promoted `PERMISSION_SYNC_ENABLED` as the canonical env var for enabling permission syncing, deprecating `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` (still respected as a fallback). [#991](https://github.com/sourcebot-dev/sourcebot/pull/991)
1318

1419
## [4.15.3] - 2026-03-10
1520

docs/docs/configuration/config-file.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ The following are settings that can be provided in your config file to modify So
5050
| `repoGarbageCollectionGracePeriodMs` | number | 10 seconds | 1 | Grace period to avoid deleting shards while loading. |
5151
| `repoIndexTimeoutMs` | number | 2 hours | 1 | Timeout for a single repo‑indexing run. |
5252
| `enablePublicAccess` **(deprecated)** | boolean | false || Use the `FORCE_ENABLE_ANONYMOUS_ACCESS` environment variable instead. |
53-
| `experiment_repoDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 | Interval at which the repo permission syncer should run. |
54-
| `experiment_userDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 | Interval at which the user permission syncer should run. |
53+
| `repoDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 | Interval at which the repo permission syncer should run. |
54+
| `userDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 | Interval at which the user permission syncer should run. |
55+
| `experiment_repoDrivenPermissionSyncIntervalMs` **(deprecated)** | number | 24 hours | 1 | Use `repoDrivenPermissionSyncIntervalMs` instead. |
56+
| `experiment_userDrivenPermissionSyncIntervalMs` **(deprecated)** | number | 24 hours | 1 | Use `userDrivenPermissionSyncIntervalMs` instead. |
5557
| `maxAccountPermissionSyncJobConcurrency` | number | 8 | 1 | Concurrent account permission sync jobs. |
5658
| `maxRepoPermissionSyncJobConcurrency` | number | 8 | 1 | Concurrent repo permission sync jobs. |
5759

docs/docs/configuration/environment-variables.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ The following environment variables allow you to configure your Sourcebot deploy
4545
| `SOURCEBOT_EE_AUDIT_RETENTION_DAYS` | `180` | <p>The number of days to retain audit logs. Audit log records older than this will be automatically pruned daily. Set to `0` to disable pruning and retain logs indefinitely.</p> |
4646
| `AUTH_EE_GCP_IAP_ENABLED` | `false` | <p>When enabled, allows Sourcebot to automatically register/login from a successful GCP IAP redirect</p> |
4747
| `AUTH_EE_GCP_IAP_AUDIENCE` | - | <p>The GCP IAP audience to use when verifying JWT tokens. Must be set to enable GCP IAP JIT provisioning</p> |
48-
| `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` | `false` | <p>Enables [permission syncing](/docs/features/permission-syncing).</p> |
49-
| `PERMISSION_SYNC_REPO_DRIVEN_ENABLED` | `true` | <p>Enables/disables [repo-driven permission syncing](/docs/features/permission-syncing#how-it-works). Only applies when `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` is `true`.</p> |
48+
| `PERMISSION_SYNC_ENABLED` | `false` | <p>Enables [permission syncing](/docs/features/permission-syncing).</p> |
49+
| `PERMISSION_SYNC_REPO_DRIVEN_ENABLED` | `true` | <p>Enables/disables [repo-driven permission syncing](/docs/features/permission-syncing#how-it-works). Only applies when `PERMISSION_SYNC_ENABLED` is `true`.</p> |
50+
| `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` **(deprecated)** | `false` | <p>Deprecated. Use `PERMISSION_SYNC_ENABLED` instead.</p> |
5051
| `AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING` | `true` | <p>When enabled, different SSO accounts with the same email address will automatically be linked.</p> |
5152

5253

docs/docs/features/permission-syncing.mdx

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ that they have access to on the code host. Practically, this means:
1818
- Ask Sourcebot (and the underlying LLM) will only have access to repositories that the user has access to.
1919
- File browsing is scoped to the repositories that the user has access to.
2020

21-
Permission syncing can be enabled by setting the `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` environment variable to `true`.
21+
Permission syncing can be enabled by setting the `PERMISSION_SYNC_ENABLED` environment variable to `true`.
2222

2323
```bash
2424
docker run \
25-
-e EXPERIMENT_EE_PERMISSION_SYNC_ENABLED=true \
25+
-e PERMISSION_SYNC_ENABLED=true \
2626
/* additional args */ \
2727
ghcr.io/sourcebot-dev/sourcebot:latest
2828
```
@@ -97,9 +97,9 @@ Permission syncing works with **Bitbucket Cloud**. OAuth tokens must assume the
9797
- Membership in the [project that contains the repository](https://support.atlassian.com/bitbucket-cloud/docs/configure-project-permissions-for-users-and-groups/)
9898
- Membership in a group that is part of a project containing the repository
9999

100-
These users **will** still gain access via [user-driven syncing](/docs/features/permission-syncing#how-it-works), which fetches all private repositories accessible to each authenticated user. However, there may be a delay between when a repository is added and when affected users gain access in Sourcebot (up to the `experiment_userDrivenPermissionSyncIntervalMs` interval, which defaults to 24 hours).
100+
These users **will** still gain access via [user-driven syncing](/docs/features/permission-syncing#how-it-works), which fetches all private repositories accessible to each authenticated user. However, there may be a delay between when a repository is added and when affected users gain access in Sourcebot (up to the `userDrivenPermissionSyncIntervalMs` interval, which defaults to 24 hours).
101101

102-
If your workspace relies heavily on group or project-level permissions rather than direct user grants, we recommend reducing the `experiment_userDrivenPermissionSyncIntervalMs` interval to limit the window of delay.
102+
If your workspace relies heavily on group or project-level permissions rather than direct user grants, we recommend reducing the `userDrivenPermissionSyncIntervalMs` interval to limit the window of delay.
103103
</Warning>
104104

105105
**Notes:**
@@ -120,16 +120,64 @@ Permission syncing works with **Bitbucket Data Center**. OAuth tokens must assum
120120
- Project-level permissions (inherited by all repos in the project)
121121
- Group membership
122122

123-
These users **will** still gain access via [user-driven syncing](/docs/features/permission-syncing#how-it-works), which fetches all repositories accessible to each authenticated user using the `REPO_READ` scope. However, there may be a delay between when access is granted and when affected users see the repository in Sourcebot (up to the `experiment_userDrivenPermissionSyncIntervalMs` interval, which defaults to 24 hours).
123+
These users **will** still gain access via [user-driven syncing](/docs/features/permission-syncing#how-it-works), which fetches all repositories accessible to each authenticated user using the `REPO_READ` scope. However, there may be a delay between when access is granted and when affected users see the repository in Sourcebot (up to the `userDrivenPermissionSyncIntervalMs` interval, which defaults to 24 hours).
124124

125-
If your instance relies heavily on project or group-level permissions, we recommend reducing the `experiment_userDrivenPermissionSyncIntervalMs` interval to limit the window of delay.
125+
If your instance relies heavily on project or group-level permissions, we recommend reducing the `userDrivenPermissionSyncIntervalMs` interval to limit the window of delay.
126126
</Warning>
127127

128128
**Notes:**
129129
- A Bitbucket Data Center [external identity provider](/docs/configuration/idp#bitbucket-server) must be configured to (1) correlate a Sourcebot user with a Bitbucket Data Center user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works).
130130
- The connection token must have **Repository Read** permissions so Sourcebot can read repository-level user permissions for [Repo driven syncing](/docs/features/permission-syncing#how-it-works).
131131
- OAuth tokens require the `REPO_READ` scope to list accessible repositories during [User driven syncing](/docs/features/permission-syncing#how-it-works).
132132

133+
# Manually refreshing permissions
134+
135+
If a user's permissions have changed and they need access updated immediately (without waiting for the next scheduled sync), they can trigger a manual refresh from the **Linked Accounts** page:
136+
137+
1. Navigate to **Settings → Linked Accounts**.
138+
2. Click the **Connected** button next to the relevant code host account.
139+
3. Select **Refresh Permissions** from the dropdown.
140+
141+
<Frame>
142+
<img src="/images/linked_accounts_refresh_permissions.png" alt="Linked Accounts - Refresh Permissions" />
143+
</Frame>
144+
145+
The button will show a spinner while the sync is in progress and display a confirmation once it completes.
146+
147+
148+
# Overriding enforcement per connection
149+
150+
Each [connection](/docs/connections/overview) supports an `enforcePermissions` flag that controls whether permissions are enforced for repositories in that connection. This lets you mix code hosts in a single deployment - for example, enforcing access control on a private GitHub connection while keeping an internal Gerrit instance open to all users.
151+
152+
By default, `enforcePermissions` inherits the value of `PERMISSION_SYNC_ENABLED`. You can override it per connection in the [config file](/docs/configuration/config-file):
153+
154+
```json
155+
{
156+
"connections": {
157+
"my-github": {
158+
"type": "github",
159+
"enforcePermissions": true
160+
},
161+
"my-gerrit": {
162+
"type": "gerrit",
163+
"url": "https://gerrit.example.com",
164+
"enforcePermissions": false
165+
}
166+
}
167+
}
168+
```
169+
170+
Setting `enforcePermissions: false` on a connection makes all repositories from that connection accessible to any user, regardless of the global `PERMISSION_SYNC_ENABLED` setting.
171+
172+
The table below shows when permissions are enforced based on the combination of `PERMISSION_SYNC_ENABLED` and `enforcePermissions`:
173+
174+
| `PERMISSION_SYNC_ENABLED` | `enforcePermissions` | Permissions enforced? |
175+
|--------------------------|---------------------|-----------------------|
176+
| `true` | `true` | Yes |
177+
| `true` | `false` | No |
178+
| `false` | `true` | No |
179+
| `false` | `false` | No |
180+
133181
# How it works
134182

135183
Permission syncing works by periodically syncing ACLs from the code host(s) to Sourcebot to build an internal mapping between Users and Repositories. This mapping is hydrated in two directions:
@@ -146,19 +194,5 @@ The sync intervals can be configured using the following settings in the [config
146194

147195
| Setting | Type | Default | Minimum |
148196
|-------------------------------------------------|---------|------------|---------|
149-
| `experiment_repoDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 |
150-
| `experiment_userDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 |
151-
152-
## Manually refreshing permissions
153-
154-
If a user's permissions have changed and they need access updated immediately (without waiting for the next scheduled sync), they can trigger a manual refresh from the **Linked Accounts** page:
155-
156-
1. Navigate to **Settings → Linked Accounts**.
157-
2. Click the **Connected** button next to the relevant code host account.
158-
3. Select **Refresh Permissions** from the dropdown.
159-
160-
<Frame>
161-
<img src="/images/linked_accounts_refresh_permissions.png" alt="Linked Accounts - Refresh Permissions" />
162-
</Frame>
163-
164-
The button will show a spinner while the sync is in progress and display a confirmation once it completes.
197+
| `repoDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 |
198+
| `userDrivenPermissionSyncIntervalMs` | number | 24 hours | 1 |

docs/snippets/schemas/v3/azuredevops.schema.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@
189189
}
190190
},
191191
"additionalProperties": false
192+
},
193+
"enforcePermissions": {
194+
"type": "boolean",
195+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
192196
}
193197
},
194198
"required": [

docs/snippets/schemas/v3/bitbucket.schema.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@
162162
}
163163
},
164164
"additionalProperties": false
165+
},
166+
"enforcePermissions": {
167+
"type": "boolean",
168+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
165169
}
166170
},
167171
"required": [

docs/snippets/schemas/v3/connection.schema.mdx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@
205205
}
206206
},
207207
"additionalProperties": false
208+
},
209+
"enforcePermissions": {
210+
"type": "boolean",
211+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
208212
}
209213
},
210214
"required": [
@@ -407,6 +411,10 @@
407411
}
408412
},
409413
"additionalProperties": false
414+
},
415+
"enforcePermissions": {
416+
"type": "boolean",
417+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
410418
}
411419
},
412420
"required": [
@@ -562,6 +570,10 @@
562570
}
563571
},
564572
"additionalProperties": false
573+
},
574+
"enforcePermissions": {
575+
"type": "boolean",
576+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
565577
}
566578
},
567579
"required": [
@@ -669,6 +681,10 @@
669681
}
670682
},
671683
"additionalProperties": false
684+
},
685+
"enforcePermissions": {
686+
"type": "boolean",
687+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
672688
}
673689
},
674690
"required": [
@@ -839,6 +855,10 @@
839855
}
840856
},
841857
"additionalProperties": false
858+
},
859+
"enforcePermissions": {
860+
"type": "boolean",
861+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
842862
}
843863
},
844864
"required": [
@@ -1047,6 +1067,10 @@
10471067
}
10481068
},
10491069
"additionalProperties": false
1070+
},
1071+
"enforcePermissions": {
1072+
"type": "boolean",
1073+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
10501074
}
10511075
},
10521076
"required": [
@@ -1116,6 +1140,10 @@
11161140
}
11171141
},
11181142
"additionalProperties": false
1143+
},
1144+
"enforcePermissions": {
1145+
"type": "boolean",
1146+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
11191147
}
11201148
},
11211149
"required": [

docs/snippets/schemas/v3/genericGitHost.schema.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
}
6161
},
6262
"additionalProperties": false
63+
},
64+
"enforcePermissions": {
65+
"type": "boolean",
66+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
6367
}
6468
},
6569
"required": [

docs/snippets/schemas/v3/gerrit.schema.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@
100100
}
101101
},
102102
"additionalProperties": false
103+
},
104+
"enforcePermissions": {
105+
"type": "boolean",
106+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
103107
}
104108
},
105109
"required": [

docs/snippets/schemas/v3/gitea.schema.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@
148148
}
149149
},
150150
"additionalProperties": false
151+
},
152+
"enforcePermissions": {
153+
"type": "boolean",
154+
"description": "Controls whether repository permissions are enforced for this connection. When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect. Defaults to the value of `PERMISSION_SYNC_ENABLED`. See https://docs.sourcebot.dev/docs/features/permission-syncing"
151155
}
152156
},
153157
"required": [

0 commit comments

Comments
 (0)