/** * _sync_credentials reserved collection — * * Stores per-adapter OAuth tokens (and any other long-lived sync secrets) as * encrypted records inside the vault itself. Tokens are wrapped with the * compartment's own DEK, live on disk as ciphertext like any other record, and * are accessed only through the dedicated API in this module — never via * `vault.collection('_sync_credentials')`. * * Design decisions * ──────────────── * * **Why a reserved collection, not a separate store?** * The compartment's existing encryption stack (AES-256-GCM + collection DEK) * is exactly the right primitive for protecting OAuth tokens at rest. Using a * separate store would require a new encryption surface, new adapter calls, * and a new backup/restore path — all of which already exist for collections. * * **Why not exposed as a regular collection?** * The same reason `_keyring` and `_ledger` aren't: they have invariants that * must be enforced (naming scheme, no cross-user leakage, no schema * validation, no history/ledger writes for privacy). Routing through a * dedicated API enforces those invariants. * * **Token lifecycle:** * - `putCredential(vault, adapterId, token)` — store or overwrite * - `getCredential(vault, adapterId)` — load and decrypt * - `deleteCredential(vault, adapterId)` — remove * - `listCredentials(vault)` — enumerate adapter IDs (not tokens) * * The `adapterId` is the record ID within the `_sync_credentials` collection. * It should be a stable, human-readable identifier for the adapter instance * (e.g. `'google-drive'`, `'dropbox'`, `'s3-prod'`). * * **ACL:** only `owner` and `admin` roles can read/write sync credentials. * Operators, viewers, and clients cannot call this API. The check is made * against the caller's keyring role at call time. */ import type { NoydbStore } from '../../kernel/types.js'; import type { UnlockedKeyring } from './keyring.js'; /** * The reserved collection name. Never collides with user collections. * Canonical definition lives in `reserved-secret-collections.ts` (shared with * the `vault.collection()` guard and grant DEK-propagation); re-exported here * for existing consumers. */ export { SYNC_CREDENTIALS_COLLECTION } from './reserved-secret-collections.js'; /** * An OAuth/auth token stored in `_sync_credentials`. * * Fields mirror the OAuth2 token response shape. `customData` is an escape * hatch for adapter-specific secrets (API keys, connection strings, etc.) * that don't fit the OAuth2 shape. */ export interface SyncCredential { /** Stable identifier for the adapter instance (e.g. 'google-drive'). */ readonly adapterId: string; /** OAuth token type, usually 'Bearer'. */ readonly tokenType: string; /** The access token. Expires at `expiresAt` if set. */ readonly accessToken: string; /** Long-lived refresh token for renewing the access token. */ readonly refreshToken?: string; /** ISO timestamp when `accessToken` expires. Absent means "no expiry". */ readonly expiresAt?: string; /** Space-separated OAuth scopes. */ readonly scopes?: string; /** Adapter-specific opaque data (API keys, endpoints, etc.). */ readonly customData?: Record; } /** * Store or overwrite a sync credential for the given adapter. * * The credential is encrypted with the `_sync_credentials` collection DEK * (auto-generated on first use). The record ID is the `adapterId`. * * Requires owner or admin role. */ export declare function putCredential(adapter: NoydbStore, vault: string, keyring: UnlockedKeyring, credential: SyncCredential): Promise; /** * Load and decrypt a sync credential for the given adapter ID. * * Returns `null` if no credential exists for this adapter. * Requires owner or admin role. */ export declare function getCredential(adapter: NoydbStore, vault: string, keyring: UnlockedKeyring, adapterId: string): Promise; /** * Delete a sync credential by adapter ID. * * No-op if the credential doesn't exist. Requires owner or admin role. */ export declare function deleteCredential(adapter: NoydbStore, vault: string, keyring: UnlockedKeyring, adapterId: string): Promise; /** * List all adapter IDs that have stored credentials. * * Returns only the IDs, never the credential payloads. Useful for * displaying "connected adapters" in UI without decrypting tokens. * Requires owner or admin role. */ export declare function listCredentials(adapter: NoydbStore, vault: string, keyring: UnlockedKeyring): Promise; /** * Check whether a credential exists and whether its access token has expired. * * Returns `{ exists: false }` if no credential is stored, or * `{ exists: true, expired: boolean }` based on the `expiresAt` field. * Requires owner or admin role. */ export declare function credentialStatus(adapter: NoydbStore, vault: string, keyring: UnlockedKeyring, adapterId: string): Promise<{ exists: false; } | { exists: true; expired: boolean; }>;