You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A connected source rotates its credential in place: same source, same collection, verified by a read before it replaces the old — today a rotated key is a different source and a full re-ingest #1225
Read from main at 877e8a28 on 2026-10-23, as phase 4 of obin-ai/handbook design/bucket-datasources/roadmap.md (PR obin-ai/handbook#364). Filed with the owner's go-ahead; not to start before #1211 step 3 lands (the credential path the service client settles is the one this rotates).
What we are trying to do
An organisation with a key-rotation policy rotates the access key of a connected bucket (or the token of any static-secret source) in place: same source, same collection, same grants, no re-ingest, no gap in sync.
What the code says
UpdateSourceRequest's own comment (datasource.proto, "UpdateSourceRequest changes what an already-connected source fetches, in place"): "Nothing else about a source is mutable here — the repository, the provider, the collection it writes into and its credential are each the identity of something a consumer already holds, and changing one of those is a different source." So a rotated key today means DeleteSource + AddSource: a new source id, a new collection or a re-bind, and a full re-send of the bucket under the daily reconcile. The only rotation that exists is the GitHub App installation's (datasource_installation_rotation_test.go), where the host mints the token itself.
What to build
RotateSourceCredential(org_id, source_id, credential) on DatasourceService — or a credential arm on UpdateSource if the contract's "identity" argument is overturned in the proposal; either way the source id and the collection do not change.
The new credential is verified by a read before it replaces the old (list one page of the prefix for a bucket; the equivalent probe per provider), encrypted at receipt through the same SecretCipher path, written as a new envelope under the same DatasourceConnectorSecretPurpose; the old envelope is retired, never overwritten in place, so a failed verification leaves the source exactly as it was.
Audited as its own typed event (saas.datasource.source.credential_rotated, no material in the payload), and Datasource projects credential_rotated_at.
The kit: a "Rotate key" action on the source, refusal shown by field.
Tests before review
Rotation round-trip on real Postgres (new envelope readable, old retired); a failed probe leaves the old envelope live and the source ACTIVE; an in-flight sync completes on the old key while the next uses the new (fake connector); audit event emitted with no secret material; UpdateSource still refuses a credential if that arm is not taken.
Activity