@framers/agentos-ext-account-sources 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @framers/agentos-ext-account-sources
2
2
 
3
- A person's own accounts as sources. This library defines `AccountSourceConnector` and the `SourceRef` every passage carries, with helpers for line ranges, links, bounded body reads, requests under the shared public-sources limiter and the cache lifetime of a read, the contracts, pure rules, PostgreSQL store and coordinator of a durable, resumable synchronization, and weighted request quotas that every worker shares.
3
+ A person's own accounts as sources. This library defines `AccountSourceConnector` and the `SourceRef` every passage carries, with helpers for line ranges, links, bounded body reads, requests under the shared public-sources limiter and the cache lifetime of a read, the contracts, pure rules, PostgreSQL store and coordinator of a durable, resumable synchronization, weighted request quotas that every worker shares, and the fenced purge and revocation recovery of a connection's grant.
4
4
 
5
5
  Install `@framers/agentos-ext-account-sources` with its peer `@framers/agentos` 0.13.134 or later. Node.js 20.19 or later is required. The runtime dependencies are `@framers/agentos-ext-public-sources` and `@framers/sql-storage-adapter`.
6
6
 
@@ -15,7 +15,7 @@ An `AccountSourceConnector` exposes its `kind` and four operations:
15
15
 
16
16
  A selection holds account item ids. A read carries text or bytes with a media type, the item and its attribution. A gone item names its id and the reason: `removed`, `no_access` or `not_owned`.
17
17
 
18
- Connectors obtain their grants through AgentOS's auth module and store them through its sealed token store. This library holds no grants and performs no sealing.
18
+ Connectors obtain their grants through AgentOS's auth module and store them through its sealed token store. The connector interface holds no grant. The revocation lifecycle seals a stopped grant's refresh token into its revoke-only queue with the same module's sealing (see [Revocation and purge](#revocation-and-purge)).
19
19
 
20
20
  ## Attribution
21
21
 
@@ -293,7 +293,7 @@ for (const sql of sourceSyncCoordinatorSchema()) await db.exec(sql);
293
293
 
294
294
  const sync = new AccountSourceSync<PreparedPassages>({
295
295
  store, // the SqlAccountSourceSyncStore above
296
- lifecycle, // the host's purge recovery and grant-loss handoff
296
+ lifecycle, // purge recovery and the grant-loss handoff: a SourceRevocations (below)
297
297
  // Builds the connector bound to this lease; it sends no request while building.
298
298
  provider: async (currentHandle) => connectorFor(scope, currentHandle).sync,
299
299
  // Embeds outside any transaction; the store commits the result with its document.
@@ -339,6 +339,72 @@ With an `events` sink the coordinator reports `started` at the claim, `progress`
339
339
 
340
340
  The coordinator uses three store methods besides those above. `reserveOperation(handle, key)` counts an attempt at the anchor read, a reconcile's look at the committed cursor or a page before it is sent, and answers `{ kind: 'attempt', attempt }`, or `{ kind: 'deferred', reason, retryAt }` while a recorded wait has not passed or after five attempts that recorded nothing. `deferOperation(handle, key, attempt, reason, retryAt)` records the wait after a retriable failure when that attempt is still the one reserved. `pendingRetry(handle)` answers the earliest wait among the cycle's reads.
341
341
 
342
+ ### Revocation and purge
343
+
344
+ ```ts
345
+ import { SourceRevocations, sourceRevocationSchema } from '@framers/agentos-ext-account-sources';
346
+
347
+ // The host's migration, with the store's and the coordinator's schemas.
348
+ for (const sql of sourceRevocationSchema()) await db.exec(sql);
349
+
350
+ const revocations = new SourceRevocations<PreparedPassages>(db, {
351
+ store, // the SqlAccountSourceSyncStore above
352
+ keys, // SealingKeys: the current key seals, previous keys still open
353
+ values: (tx, scope) => grantRows(tx, scope), // the host's sealed-value rows, on the transaction given
354
+ revoker: (signal) => revocationFlow(signal), // a provider flow without a store; its fetch honors the signal
355
+ });
356
+ // It is the coordinator's lifecycle binding: new AccountSourceSync({ store, lifecycle: revocations, ... }).
357
+
358
+ // A disconnect from an authenticated request, at the generation that request saw.
359
+ const notice = await revocations.disconnect(scope, generation);
360
+ // null when the generation moved on; otherwise { localPending, revocation }
361
+
362
+ // An account-independent job, at least once a minute.
363
+ await revocations.revokeBatch();
364
+ ```
365
+
366
+ `SourceRevocations` stops a connection and completes what the stop leaves: the purge of its sources and of every result derived from them, and the revocation of its grant at the provider. `sourceRevocationSchema(prefix)` answers eight statements for the host's migration, under a prefix of a lower-case letter and at most 27 lower-case letters, digits or underscores (`agentos_source_revoke` unless given): the owner's block marker, each connection's stop intent, the revoke-only queue and its due index, the grant lease, and the authorization exchanges with two indexes. No table references an account table, so removing an account cannot erase an outstanding revocation, and the rows hold opaque ids, times, fixed states, attempt counts and sealed refresh tokens only. The service needs a PostgreSQL adapter with transactions and runs no DDL.
367
+
368
+ The host wires it in four places. The store's `active` binding answers true only when the account is live and `permits(tx, owner)` holds, so an owner whose stop is still purging, or who was deleted, has no sources. Authorization start and grant installation check `canConnect(tx, owner)` under the owner fence. The grant is saved with `new SealedTokenStore(values(tx, scope), keys, { namespace: sourceGrantNamespace(scope) })` under `SOURCE_GRANT_KEY`, the row every lifecycle step reads. A grant operation such as a refresh runs through `withGrant`.
369
+
370
+ `disconnect(scope, generation)`, `loseGrant(handle, reason)` and `deleteOwner(owner)` each commit in one short transaction the owner's fence, the connection's stop (`disconnected`, or `needs_reconnecting` for a lost grant), a purge barrier, a content-free stop intent, the grant's refresh token moved into the revoke-only queue, sealed under the current key and bound to its queue row, and the deletion of the active grant and its lease. `disconnect` takes the generation the authenticated request saw and answers null for another one; `loseGrant` takes the coordinator's exact current handle and answers false for a stale one; `deleteOwner` stops every connection of the owner, also when there is none, and keeps the owner blocked for good. Call it before the host removes any source, credential or account row. A grant that cannot be opened is deleted all the same, and its queue row records it as unreadable. The purge then runs in a transaction of its own; when it fails, the committed stop keeps the owner blocked, and `recover(scope)` completes it. The coordinator's `maintain` calls `recover` for every connection, paused ones included. None of these methods calls the provider.
371
+
372
+ `revokeBatch(limit?, signal?)` is the account-independent worker. It claims due queue rows, one worker per row, opens each token and revokes it with the revoker's `revoke` outside any transaction, within 15 seconds. A success confirms the row and erases its ciphertext. A failure is retried after 1, 2, 4, 8 and then 15 minutes, whatever status the provider answered. Each token's deadline is 23 hours and 59 minutes after it was queued, and neither a retry, a restart nor a repeated disconnect or deletion moves it: at the deadline the ciphertext is erased and the row records that the permission must be removed at the provider. A token that does not open, because its ciphertext was moved to another row or its key is gone, is erased the same way and never sent. A worker whose claim expired cannot settle a row another worker claimed since. The batch answers `{ attempted, confirmed, deferred, expired, unreadable }`. The database erases nothing on its own when a deadline passes: reads and dispatch stop at the deadline, and erasure needs the worker to run, so schedule it at least once a minute.
373
+
374
+ `status(scope)` answers `{ localPending, revocation }`, with `revocation` one of `not_needed`, `pending`, `confirmed` and `permission_removal_required`; the local purge completes whatever the revocation's outcome. A provider can revoke a whole grant rather than one connection's, so `canConnect` stays false while a revocation of the owner's is pending, or expired or unreadable and not yet acknowledged. `acknowledgeRemoval(scope)` records the account owner's statement that the permission was removed at the provider, after which a new grant may begin; it is an attestation, never a confirmation. `compact()` removes, a day after their deadline, terminal rows that nothing refers to.
375
+
376
+ `withGrant(handle, work, signal?)` runs one grant operation under a durable 30-second grant lease, without holding a transaction over the network; another worker gets `rate_limited` until the lease expires, and the lease is never renewed. `work` receives a `SealedTokenStore` and a signal that aborts after 15 seconds. Every load and save through that store, and the final return, recheck the sync handle and the lease, so after a disconnect or once the lease has expired a held token can be neither saved nor returned (`grant_rejected`).
377
+
378
+ ```ts
379
+ // The callback's transaction consumes the authenticated start and records the exchange, or rolls back.
380
+ const exchange = await db.transaction(async (tx) => {
381
+ const start = await consumeStart(tx, callback);
382
+ const begun = start && await revocations.beginExchange(tx, scope, start.generation, start.exchangeId);
383
+ if (!begun) throw new Error('start_refused');
384
+ return begun;
385
+ });
386
+ // Only after that commit does the code exchange reach the provider.
387
+ // When it returns no grant: endExchange(exchange, 'not_dispatched' | 'revoked' | 'unconfirmed').
388
+ const grant = await exchangeCode(callback);
389
+ const outcome = await revocations.settleExchange(exchange, grant, async (tx, granted) => {
390
+ await new SealedTokenStore(grantRows(tx, scope), keys, { namespace: sourceGrantNamespace(scope) })
391
+ .save(SOURCE_GRANT_KEY, granted);
392
+ await new SqlAccountSourceSyncStore(tx, { bindings }).control(scope, { type: 'grant_ready', use });
393
+ return finishStart(tx, exchange.id);
394
+ });
395
+ // outcome.kind: 'installed', 'cleanup_pending' or 'permission_removal_required'
396
+ ```
397
+
398
+ An authorization code exchange returns a grant before anything is installed, and a cancellation, a disconnect, the account's deletion or a failed installation must not leave that grant at the provider unrecorded. `beginExchange(tx, scope, generation, exchangeId)` runs in the host transaction that consumes the authenticated start. It answers the exchange only when the connection is `connecting` at that generation and `canConnect` holds, so one exchange is in flight per owner; otherwise it answers null, and the host rolls the consumption back. The exchange's row holds opaque ids and times, never a code, a state, a PKCE verifier, an address or a credential. The exchange ends 60 seconds after it begins, and while it is open `canConnect` is false.
399
+
400
+ `settleExchange(exchange, grant, install)` first seals the grant's refresh token into the revoke-only queue, held there until the exchange ends, so a crash cannot lose it. When `mayInstallExchange(tx, exchange)` holds (this exact exchange is open and before its end, the connection is still `connecting` at its generation with no grant installed, and nothing else keeps the owner from a new grant), `install` runs in the owner's transaction: it saves the grant, records `grant_ready` and finishes the start, and the same commit removes the held token. When the exchange may not install, or `install` answers anything but true, throws or finishes after the end, the whole installation rolls back and the held token is due for revocation at once; after a crash it is due when the exchange ends, and `revokeBatch` revokes it under its own deadline. A grant that arrives for an exchange already recorded as unconfirmed is neither installed nor revoked: a newer grant at the provider could share its permission, so the notice stands.
401
+
402
+ `endExchange(exchange, outcome)` records an exchange that returned no grant: `not_dispatched` when the request never left the process, `revoked` when the provider flow revoked a grant it refused and the provider confirmed it, and `unconfirmed` otherwise, as after a timeout, an aborted connection or an unreadable answer. An open exchange whose end passes with nothing recorded, because its worker stopped, becomes unconfirmed at the next `revokeBatch`, counted in `expired`. A recorded outcome is never weakened. An unconfirmed exchange, or a returned grant whose revocation expired or could not be opened, makes `status` answer `permission_removal_required` and keeps `canConnect` false until `acknowledgeRemoval`. Exchange rows survive the account's deletion, and `compact` removes them a day after their end once nothing about them is pending.
403
+
404
+ With an `events` sink the service reports `fenced` after a stop's transaction, `purged` after its purge, and `confirmed`, `deferred`, `expired` and `unreadable` from `revokeBatch`, each as `{ kind: 'source_revocation', state, n }`, with no scope, token or provider text. A sink that throws is ignored.
405
+
406
+ The service uses five store methods besides those above. `stageStop(scope, event, stage, expected)` and `stageOwnerStop(owner, stage)` commit a stop and run the service's writes in its transaction; `completeStop(scope, generation, complete)` runs the purge's completion once the barrier is gone; `withCurrentHandle(handle, work)` runs grant storage work with the handle checked; and `lockState(tx, scope)` reads the connection's state in a host transaction after taking the owner fence's lock and then the connection's.
407
+
342
408
  ## License
343
409
 
344
410
  Apache-2.0. This pack carries a copy of the repository's license.
package/dist/index.d.ts CHANGED
@@ -19,4 +19,6 @@ export { SourceQuota, sourceQuotaSchema, sourceRetryAt, type SourceQuotaMethod,
19
19
  /** The resumable synchronization coordinator, its lifecycle binding and the migration of its control state. */
20
20
  export { AccountSourceSync, type AccountSourceSyncOptions, type SourceSyncLifecycle, type SourceSyncRunResult, type SourceSyncMaintenanceResult, } from './sync/AccountSourceSync.js';
21
21
  export { sourceSyncCoordinatorSchema, type SourceSyncOperationAttempt, type SourceSyncOperationState, } from './sync/coordinatorState.js';
22
+ /** Fenced purge, the grant lease, the bounded revoke-only queue and the cleanup of grants that exchanges return. */
23
+ export { SourceRevocations, sourceRevocationSchema, sourceGrantNamespace, SOURCE_GRANT_KEY, type SourceRevocationOptions, type SourceRevocationReason, type SourceRevocationNotice, type SourceRevocationBatch, type SourceRevocationEvent, type SourceGrantExchange, type SourceGrantExchangeOutcome, type SourceUninstalledGrantLifecycle, } from './sync/SourceRevocations.js';
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAC3B,yDAAyD;AACzD,cAAc,gBAAgB,CAAC;AAC/B,8DAA8D;AAC9D,cAAc,iBAAiB,CAAC;AAChC,oEAAoE;AACpE,cAAc,cAAc,CAAC;AAC7B,0EAA0E;AAC1E,cAAc,kBAAkB,CAAC;AACjC,yEAAyE;AACzE,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,wGAAwG;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,KAAK,yBAAyB,EACvF,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAC3E,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,GAC3E,MAAM,qCAAqC,CAAC;AAC7C,2GAA2G;AAC3G,OAAO,EACL,WAAW,EAAE,iBAAiB,EAAE,aAAa,EAC7C,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EACvE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,GACpG,MAAM,uBAAuB,CAAC;AAC/B,+GAA+G;AAC/G,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EACvD,KAAK,mBAAmB,EAAE,KAAK,2BAA2B,GAC3D,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,EAAE,KAAK,wBAAwB,GAC/D,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAC3B,yDAAyD;AACzD,cAAc,gBAAgB,CAAC;AAC/B,8DAA8D;AAC9D,cAAc,iBAAiB,CAAC;AAChC,oEAAoE;AACpE,cAAc,cAAc,CAAC;AAC7B,0EAA0E;AAC1E,cAAc,kBAAkB,CAAC;AACjC,yEAAyE;AACzE,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,wGAAwG;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EAAE,KAAK,yBAAyB,EACvF,KAAK,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAC3E,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,iBAAiB,GAC3E,MAAM,qCAAqC,CAAC;AAC7C,2GAA2G;AAC3G,OAAO,EACL,WAAW,EAAE,iBAAiB,EAAE,aAAa,EAC7C,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EACvE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,GACpG,MAAM,uBAAuB,CAAC;AAC/B,+GAA+G;AAC/G,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAAE,KAAK,mBAAmB,EACvD,KAAK,mBAAmB,EAAE,KAAK,2BAA2B,GAC3D,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,2BAA2B,EAC3B,KAAK,0BAA0B,EAAE,KAAK,wBAAwB,GAC/D,MAAM,4BAA4B,CAAC;AACpC,oHAAoH;AACpH,OAAO,EACL,iBAAiB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,gBAAgB,EACjF,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAAE,KAAK,sBAAsB,EACtF,KAAK,qBAAqB,EAAE,KAAK,qBAAqB,EACtD,KAAK,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,KAAK,+BAA+B,GAChG,MAAM,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -19,4 +19,6 @@ export { SourceQuota, sourceQuotaSchema, sourceRetryAt, } from './sync/SourceQuo
19
19
  /** The resumable synchronization coordinator, its lifecycle binding and the migration of its control state. */
20
20
  export { AccountSourceSync, } from './sync/AccountSourceSync.js';
21
21
  export { sourceSyncCoordinatorSchema, } from './sync/coordinatorState.js';
22
+ /** Fenced purge, the grant lease, the bounded revoke-only queue and the cleanup of grants that exchanges return. */
23
+ export { SourceRevocations, sourceRevocationSchema, sourceGrantNamespace, SOURCE_GRANT_KEY, } from './sync/SourceRevocations.js';
22
24
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAC3B,yDAAyD;AACzD,cAAc,gBAAgB,CAAC;AAC/B,8DAA8D;AAC9D,cAAc,iBAAiB,CAAC;AAChC,oEAAoE;AACpE,cAAc,cAAc,CAAC;AAC7B,0EAA0E;AAC1E,cAAc,kBAAkB,CAAC;AACjC,yEAAyE;AACzE,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,wGAAwG;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,yBAAyB,GAI1B,MAAM,qCAAqC,CAAC;AAC7C,2GAA2G;AAC3G,OAAO,EACL,WAAW,EAAE,iBAAiB,EAAE,aAAa,GAG9C,MAAM,uBAAuB,CAAC;AAC/B,+GAA+G;AAC/G,OAAO,EACL,iBAAiB,GAGlB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,2BAA2B,GAE5B,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,cAAc,YAAY,CAAC;AAC3B,yDAAyD;AACzD,cAAc,gBAAgB,CAAC;AAC/B,8DAA8D;AAC9D,cAAc,iBAAiB,CAAC;AAChC,oEAAoE;AACpE,cAAc,cAAc,CAAC;AAC7B,0EAA0E;AAC1E,cAAc,kBAAkB,CAAC;AACjC,yEAAyE;AACzE,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,wGAAwG;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,yBAAyB,GAI1B,MAAM,qCAAqC,CAAC;AAC7C,2GAA2G;AAC3G,OAAO,EACL,WAAW,EAAE,iBAAiB,EAAE,aAAa,GAG9C,MAAM,uBAAuB,CAAC;AAC/B,+GAA+G;AAC/G,OAAO,EACL,iBAAiB,GAGlB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,2BAA2B,GAE5B,MAAM,4BAA4B,CAAC;AACpC,oHAAoH;AACpH,OAAO,EACL,iBAAiB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,gBAAgB,GAIlF,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,311 @@
1
+ /**
2
+ * @fileoverview Fenced purge and revocation recovery for account sources. One short transaction blocks the owner,
3
+ * moves the connection, records a purge marker and moves the grant's refresh token into a sealed revoke-only queue;
4
+ * the purge completes in a recoverable transaction of its own; an account-independent worker revokes queued tokens
5
+ * before a fixed deadline. A durable grant lease serializes refreshes across workers, and the grant an authorization
6
+ * exchange returns is either installed or queued for revocation, never lost.
7
+ * @module account-sources/sync/SourceRevocations
8
+ */
9
+ import type { StorageAdapter } from '@framers/sql-storage-adapter';
10
+ import type { SourceOwnerScope } from '@framers/agentos/cognition/library';
11
+ import { SealedTokenStore, type OAuthTokenSet, type SealedBytesStore, type SealingKeys, type RedirectOAuthFlow } from '@framers/agentos/auth';
12
+ import type { SourceSyncScope } from './types.js';
13
+ import type { SqlAccountSourceSyncStore, SourceSyncHandle } from './SqlAccountSourceSyncStore.js';
14
+ import type { SourceSyncLifecycle } from './AccountSourceSync.js';
15
+ /** The key a connection's grant is kept under in the host's sealed-value rows, inside its `sourceGrantNamespace`. */
16
+ export declare const SOURCE_GRANT_KEY = "grant";
17
+ /**
18
+ * The sealed-store namespace of one connection's grant, naming its tenant, owner and connection: a sealed grant
19
+ * copied to another connection's row does not open. Initial installation, refresh and the transfer to the revoke-only
20
+ * queue all use it with `SOURCE_GRANT_KEY`.
21
+ */
22
+ export declare function sourceGrantNamespace(scope: SourceSyncScope): string;
23
+ /** Why a connection stopped: a disconnect, the account's deletion, a refused grant or a grant without its scope. */
24
+ export type SourceRevocationReason = 'disconnect' | 'account_deleted' | 'grant_rejected' | 'insufficient_scope';
25
+ /**
26
+ * A connection's lifecycle notice for authenticated controls: whether its local purge is still pending, and where
27
+ * revoking its grant stands. `not_needed` when there was no grant to revoke, `pending` while the queue holds it,
28
+ * `confirmed` once the provider answered the revocation with a success, and `permission_removal_required` when it
29
+ * could not be revoked, so the account owner removes the permission at the provider. The local purge completes
30
+ * whatever the revocation's outcome.
31
+ */
32
+ export interface SourceRevocationNotice {
33
+ readonly localPending: boolean;
34
+ readonly revocation: 'not_needed' | 'pending' | 'confirmed' | 'permission_removal_required';
35
+ }
36
+ /**
37
+ * What one `revokeBatch` did: the revocations it sent, those confirmed, those deferred to a retry, the queued tokens
38
+ * erased at their deadline together with the exchanges whose deadline passed with no outcome, and the queued tokens
39
+ * that could not be opened.
40
+ */
41
+ export interface SourceRevocationBatch {
42
+ readonly attempted: number;
43
+ readonly confirmed: number;
44
+ readonly deferred: number;
45
+ readonly expired: number;
46
+ readonly unreadable: number;
47
+ }
48
+ /** A content-free lifecycle event: a fixed state and a count, never a scope, a token or a provider's text. */
49
+ export interface SourceRevocationEvent {
50
+ readonly kind: 'source_revocation';
51
+ readonly state: 'fenced' | 'purged' | 'confirmed' | 'deferred' | 'expired' | 'unreadable';
52
+ readonly n: number;
53
+ }
54
+ /**
55
+ * What the lifecycle runs with: the synchronization store, the sealing keys, the host's sealed-value binding and a
56
+ * revocation-only provider flow, with an optional table prefix, clock and event sink.
57
+ *
58
+ * @typeParam Prepared - The store's prepared value for one document.
59
+ */
60
+ export interface SourceRevocationOptions<Prepared> {
61
+ /** Prefix of the lifecycle tables, the one given to `sourceRevocationSchema`; `agentos_source_revoke` unless given. */
62
+ readonly tablePrefix?: string;
63
+ /** The synchronization store whose connections the lifecycle stops and purges. */
64
+ readonly store: SqlAccountSourceSyncStore<Prepared>;
65
+ /** The current key seals queued tokens; it and every previous key open them and the host's grants. */
66
+ readonly keys: SealingKeys;
67
+ /** Same sealed-value table used by initial grant installation; every operation uses tx. */
68
+ readonly values: (tx: StorageAdapter, scope: SourceSyncScope) => SealedBytesStore;
69
+ /** Construct a null-store provider flow. Its capped fetch must honor this signal. */
70
+ readonly revoker: (signal: AbortSignal) => Pick<RedirectOAuthFlow, 'revoke'>;
71
+ /** Current time as integer UTC epoch milliseconds; `Date.now` unless given. */
72
+ readonly clock?: () => number;
73
+ /** Receives content-free events; a sink that throws is ignored. */
74
+ readonly events?: (event: SourceRevocationEvent) => void;
75
+ }
76
+ /**
77
+ * One authorization code exchange begun for a connection: an opaque id, the connection, the generation its grant
78
+ * may be installed into, and when it began and ends, in UTC epoch milliseconds. It holds no authorization code,
79
+ * state, PKCE verifier, address or credential.
80
+ */
81
+ export interface SourceGrantExchange {
82
+ readonly id: string;
83
+ readonly scope: SourceSyncScope;
84
+ readonly generation: number;
85
+ readonly startedAt: number;
86
+ readonly endsAt: number;
87
+ }
88
+ /**
89
+ * What settling an exchange did with its grant: `installed` in the connection's sealed binding, `cleanup_pending`
90
+ * when the grant went to the revoke-only queue instead, and `permission_removal_required` when it could be neither,
91
+ * so the account owner removes the permission at the provider.
92
+ */
93
+ export type SourceGrantExchangeOutcome = {
94
+ readonly kind: 'installed';
95
+ } | {
96
+ readonly kind: 'cleanup_pending';
97
+ } | {
98
+ readonly kind: 'permission_removal_required';
99
+ };
100
+ /**
101
+ * The lifecycle of a grant an authorization exchange returns before it is installed: the exchange is recorded before
102
+ * its request is sent, the returned grant is sealed into the revoke-only queue before it is installed, and a grant
103
+ * that is not installed is revoked from that queue or reported as needing the account owner's removal.
104
+ */
105
+ export interface SourceUninstalledGrantLifecycle {
106
+ beginExchange(tx: StorageAdapter, scope: SourceSyncScope, generation: number, exchangeId: string): Promise<SourceGrantExchange | null>;
107
+ settleExchange(exchange: SourceGrantExchange, grant: OAuthTokenSet, install: (tx: StorageAdapter, grant: OAuthTokenSet) => Promise<boolean>): Promise<SourceGrantExchangeOutcome>;
108
+ endExchange(exchange: SourceGrantExchange, outcome: 'not_dispatched' | 'revoked' | 'unconfirmed'): Promise<void>;
109
+ mayInstallExchange(tx: StorageAdapter, exchange: SourceGrantExchange): Promise<boolean>;
110
+ }
111
+ /**
112
+ * PostgreSQL DDL for the migration-owned lifecycle tables: the owner's block marker, the per-connection stop
113
+ * intent, the revoke-only queue and its due index, the grant lease, and the authorization exchanges with their two
114
+ * indexes. The service never runs them: the host's migration does. No table references an account table, so
115
+ * removing an account cannot erase an outstanding revocation, and the rows hold opaque ids, times, fixed states,
116
+ * attempt counts and sealed refresh tokens only.
117
+ *
118
+ * @param prefix - Table-name prefix: a lower-case letter, then at most 27 lower-case letters, digits or underscores.
119
+ * @returns The eight statements, in the order they must run.
120
+ * @throws {Error} `invalid_source_revocation_prefix` when the prefix does not have that form.
121
+ */
122
+ export declare function sourceRevocationSchema(prefix?: string): readonly string[];
123
+ /**
124
+ * Adapter-owned lifecycle recovery. No method logs or returns a queued credential.
125
+ *
126
+ * `disconnect`, `loseGrant` and `deleteOwner` commit the owner's fence, the connection's stop, a purge marker and the
127
+ * transfer of the grant's refresh token to the revoke-only queue in one transaction, before any provider call; the
128
+ * purge of sources and every result derived from them completes in a transaction of its own, which `recover`
129
+ * resumes after a failure. `revokeBatch` revokes queued tokens on its own schedule, independent of accounts, until
130
+ * each token's deadline, 23 hours and 59 minutes after it was queued. `withGrant` serializes refreshes across
131
+ * workers under a durable lease. The exchange methods record an authorization exchange before its request is sent
132
+ * and either install the grant it returns or queue it for revocation. Every check of `canConnect`, `permits` and
133
+ * `mayInstallExchange` belongs inside the owner fence of the host's transaction.
134
+ *
135
+ * @typeParam Prepared - The store's prepared value for one document.
136
+ */
137
+ export declare class SourceRevocations<Prepared = unknown> implements SourceSyncLifecycle, SourceUninstalledGrantLifecycle {
138
+ private readonly db;
139
+ private readonly options;
140
+ private readonly t;
141
+ private readonly store;
142
+ private readonly clock;
143
+ /**
144
+ * @param db - A PostgreSQL adapter with transactions, on the database of the store's tables.
145
+ * @param options - The store, keys, sealed-value binding and revoker, with an optional prefix, clock and sink.
146
+ * @throws {Error} `source_revocation_requires_postgres` for another kind of adapter or one without transactions,
147
+ * and `invalid_source_revocation_prefix` for a malformed prefix.
148
+ */
149
+ constructor(db: StorageAdapter, options: SourceRevocationOptions<Prepared>);
150
+ private now;
151
+ private emit;
152
+ private stored;
153
+ private grant;
154
+ private context;
155
+ /**
156
+ * Combine with fresh host account liveness in the sync store's active binding. False while a stop's purge is
157
+ * pending for the owner and, for good, once the owner was deleted; it does not authenticate an account.
158
+ */
159
+ permits(tx: StorageAdapter, scope: SourceOwnerScope): Promise<boolean>;
160
+ /**
161
+ * Check under the owner fence both at authorization start and grant installation. False when `permits` is, while a
162
+ * revocation of the owner's is pending or ended without confirmation and is not acknowledged, and while an exchange
163
+ * of the owner's is in flight or its returned grant awaits the same conditions.
164
+ */
165
+ canConnect(tx: StorageAdapter, scope: SourceOwnerScope): Promise<boolean>;
166
+ /**
167
+ * Whether a stop's revocation or an exchange keeps the owner from a new grant, leaving out the exchange `except`.
168
+ * A revocation pending in the queue, or one that expired or could not be opened before the owner acknowledged the
169
+ * permission's removal, keeps it; so does an exchange in flight, one whose returned grant is in the queue under the
170
+ * same conditions, and one whose outcome is unconfirmed and not acknowledged.
171
+ */
172
+ private barrier;
173
+ /**
174
+ * The connection's notice for authenticated controls: whether its purge is pending, and the more pressing of the
175
+ * revocation of its last stopped grant and the cleanup of the grant its newest exchange returned.
176
+ */
177
+ status(scope: SourceSyncScope): Promise<SourceRevocationNotice>;
178
+ private block;
179
+ private stage;
180
+ private canReplaceQueue;
181
+ /**
182
+ * Disconnects the connection at the generation the authenticated request saw: fences it, queues its grant for
183
+ * revocation and clears the active grant in one transaction, then purges its sources and every result derived
184
+ * from them. No provider call happens here.
185
+ *
186
+ * @returns The connection's notice, or null when the generation has moved on, which changes nothing.
187
+ * @throws {SourceSyncFault} `storage_unavailable` when a step fails: the committed stop stays, and `recover`
188
+ * completes its purge.
189
+ */
190
+ disconnect(scope: SourceSyncScope, generation: number): Promise<SourceRevocationNotice | null>;
191
+ /**
192
+ * The coordinator's handoff of a refused grant: with the run's exact current handle, the connection moves to
193
+ * `needs_reconnecting` and its grant is queued for revocation in one transaction, then purged as `disconnect` does.
194
+ *
195
+ * @returns Whether the handle was current and the grant was fenced; a stale handle changes nothing.
196
+ */
197
+ loseGrant(handle: SourceSyncHandle, reason: 'grant_rejected' | 'insufficient_scope'): Promise<boolean>;
198
+ /**
199
+ * Account deletion: blocks the owner for good and stops every connection of the owner, queueing each grant for
200
+ * revocation, in one transaction, also when the owner has no connection; then purges them. Call it before the host
201
+ * removes any source, credential or account row. Queued revocations outlive the account.
202
+ */
203
+ deleteOwner(scope: SourceOwnerScope): Promise<void>;
204
+ /** Recover every pending intent for the owner. No grant or account row is required. */
205
+ recover(scope: SourceSyncScope): Promise<void>;
206
+ private recoverOwner;
207
+ /**
208
+ * Owner-authenticated attestation; never describe it as provider confirmation. The account owner confirms having
209
+ * removed the permission at the provider for the connection's revocation that expired or could not be opened, and
210
+ * for its exchanges whose outcome is unconfirmed or whose returned grant's revocation ended the same way; a new
211
+ * grant may then begin. A pending or confirmed revocation is not acknowledged.
212
+ *
213
+ * @returns Whether anything was acknowledged.
214
+ */
215
+ acknowledgeRemoval(scope: SourceSyncScope): Promise<boolean>;
216
+ /**
217
+ * Runs one server-side grant operation, such as a refresh, under a durable 30-second grant lease, without holding a
218
+ * transaction over the network. `work` builds the provider flow on the sealed store it is given and must honor the
219
+ * signal, which aborts after 15 seconds or with `parent`. Every load and save through that store, and the final
220
+ * return, recheck the sync handle and the grant lease in a short transaction, so a disconnect or an expired lease
221
+ * makes a held token's save or return fail. The lease is never renewed; after it expires another worker may take
222
+ * a new one. Access tokens stay in memory.
223
+ *
224
+ * @returns What `work` answered.
225
+ * @throws {SourceSyncFault} `rate_limited` while another worker holds the lease, with its expiry as `retryAt`;
226
+ * `grant_rejected` for a stale handle, a lost lease or a disconnect; `cancelled` when `parent` aborted first;
227
+ * `storage_unavailable` for any other failure. A provider refusal (`OAuthGrantRefused`) passes through.
228
+ */
229
+ withGrant<T>(handle: SourceSyncHandle, work: (tokens: SealedTokenStore, signal: AbortSignal) => Promise<T>, parent?: AbortSignal): Promise<T>;
230
+ private expireQueue;
231
+ private claimRevoke;
232
+ private settleRevoke;
233
+ /**
234
+ * Schedule independently of accounts, source scopes and provider polling, at least every minute. Erases queued
235
+ * tokens whose deadline has passed and records exchanges whose deadline passed with no outcome as unconfirmed, then
236
+ * claims up to `limit` due tokens, one worker per token, and revokes each outside any transaction with the
237
+ * provider flow's `revoke`, 15 seconds at most. A success confirms it; a failure retries after 1, 2, 4, 8, then 15
238
+ * minutes, never past the deadline; a token that does not open is erased. A worker whose claim expired cannot
239
+ * settle a token another worker claimed since.
240
+ *
241
+ * @param limit - At most this many tokens, an integer from 1 to 100; 20 unless given.
242
+ * @param parent - Stops the batch: no request starts after it aborts.
243
+ * @throws {SourceSyncFault} `invalid_response` for another limit and `storage_unavailable` when the store fails.
244
+ */
245
+ revokeBatch(limit?: number, parent?: AbortSignal): Promise<SourceRevocationBatch>;
246
+ /**
247
+ * Remove unreferenced terminal diagnostics, never usable credentials or restore markers: a day after their
248
+ * deadline, exchanges that installed, ended, were acknowledged or whose returned grant's revocation was confirmed,
249
+ * then terminal queue rows no stop or exchange refers to, at most 1,000 of each a call.
250
+ *
251
+ * @returns How many rows were removed.
252
+ */
253
+ compact(): Promise<number>;
254
+ /** Records exchanges whose deadline passed with no grant returned as unconfirmed: their outcome is unknown. */
255
+ private lapseExchanges;
256
+ /** Takes the owner's lifecycle row lock, creating the row unblocked when missing; exchange changes take it first. */
257
+ private lockOwner;
258
+ private checkExchange;
259
+ /** The exchange's row under its lock, or null when no row matches every field of the exchange. */
260
+ private exchangeRow;
261
+ /** What a settled exchange did with its grant, from its row and its queued token's state. */
262
+ private exchangeOutcome;
263
+ /**
264
+ * Records an authorization exchange before its request is sent. Call it inside the host transaction that consumes
265
+ * the authenticated start, under the owner fence: it answers null, and the caller rolls that whole transaction
266
+ * back, unless the connection is `connecting` at `generation` and `canConnect` holds, so one exchange at a time is
267
+ * in flight for the owner and none while a revocation of the owner's is pending or unacknowledged. The exchange's
268
+ * marker holds opaque ids and times only, and the exchange ends 60 seconds after it begins: a grant returned later
269
+ * is not installed. Send the request only after the transaction commits.
270
+ *
271
+ * @param exchangeId - The host's opaque id for this attempt, 1 to 512 characters, never reused.
272
+ * @returns The exchange, or null.
273
+ * @throws {SourceSyncFault} `invalid_response` for a malformed scope, generation or id.
274
+ */
275
+ beginExchange(tx: StorageAdapter, scope: SourceSyncScope, generation: number, exchangeId: string): Promise<SourceGrantExchange | null>;
276
+ /**
277
+ * Whether this exact exchange may install its grant now, read under the owner fence in the installing transaction:
278
+ * its marker is open and holds its sealed grant for installation, its deadline has not passed, the connection is
279
+ * still `connecting` at its generation with no grant installed, `permits` holds, and no other revocation or
280
+ * exchange keeps the owner from a new grant. It recognizes only the matching live attempt, never an older or newer
281
+ * one.
282
+ */
283
+ mayInstallExchange(tx: StorageAdapter, exchange: SourceGrantExchange): Promise<boolean>;
284
+ /**
285
+ * Settles the grant an exchange returned. First the grant's refresh token is sealed into the revoke-only queue,
286
+ * held there until the exchange's end, so a crash cannot lose it. Then, when `mayInstallExchange` holds, `install`
287
+ * runs in the owner's transaction: it saves the grant under `sourceGrantNamespace` and `SOURCE_GRANT_KEY`, records
288
+ * the connection's ready state and finishes the host's start, and the same commit removes the held token. If the
289
+ * exchange may not install, `install` answers anything but true, throws or finishes after the deadline, the whole
290
+ * installation rolls back and the held token becomes due for revocation at once; if this process stops first, it
291
+ * becomes due at the exchange's end. A stale, cancelled or deleted exchange never runs `install`. An exchange that
292
+ * already ended or was recorded unconfirmed takes no token: its notice stands. A grant with no usable refresh token
293
+ * records the exchange unconfirmed.
294
+ *
295
+ * @returns What became of the grant.
296
+ * @throws {SourceSyncFault} `invalid_response` for a malformed exchange, `storage_unavailable` when the store fails.
297
+ */
298
+ settleExchange(exchange: SourceGrantExchange, grant: OAuthTokenSet, install: (tx: StorageAdapter, grant: OAuthTokenSet) => Promise<boolean>): Promise<SourceGrantExchangeOutcome>;
299
+ /**
300
+ * Records how an exchange ended when it returned no grant to settle: `not_dispatched` when its request never left
301
+ * the process, `revoked` when the provider flow revoked a grant it refused and the provider confirmed it, and
302
+ * `unconfirmed` for anything else after the request could have left, such as a timeout, an aborted connection, an
303
+ * unreadable answer or a revocation that was not confirmed. Only an open exchange holding no returned grant takes
304
+ * an ending, and a recorded outcome is never weakened. An unconfirmed exchange keeps new grants blocked until the
305
+ * account owner acknowledges the permission's removal.
306
+ *
307
+ * @throws {SourceSyncFault} `invalid_response` for a malformed exchange or another outcome.
308
+ */
309
+ endExchange(exchange: SourceGrantExchange, outcome: 'not_dispatched' | 'revoked' | 'unconfirmed'): Promise<void>;
310
+ }
311
+ //# sourceMappingURL=SourceRevocations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SourceRevocations.d.ts","sourceRoot":"","sources":["../../src/sync/SourceRevocations.ts"],"names":[],"mappings":"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"}