/** * Token store type definitions for the MCP server integration. * * Defines the core interfaces that the AES-256-GCM encryption module and * the SQLite-backed token store implement. No implementation details, no * external dependencies — pure TypeScript interfaces. * * Wire format for AES-256-GCM (Go-compatible): * [ nonce (12 bytes) ][ ciphertext (N bytes) ][ auth tag (16 bytes) ] */ /** * Strict union of supported OAuth service identifiers. * * Maps directly to the PRIMARY KEY values in the `tokens` SQLite table. * Extend this union when adding a new provider in Phase 5/6. */ export type ServiceName = 'gitlab' | 'atlassian' | 'grafana'; /** * Decrypted token data persisted per service in the `tokens` table. * * Serialised to JSON and encrypted with AES-256-GCM before being written to * the `data` BLOB column. The shape must remain byte-compatible with the Go * implementation in `padua-mcp/internal/store/sqlite.go`. * * `cloudId` and `cloudUrl` are Atlassian-specific and are omitted for GitLab * tokens. They are present when the Atlassian OAuth flow completes and the * cloud site resource ID has been resolved. */ export interface StoredToken { /** OAuth2 access token. Passed as Bearer in Authorization headers. */ accessToken: string; /** OAuth2 refresh token. Used to obtain a new access token on expiry. */ refreshToken: string; /** * Token type returned by the provider's token endpoint. * Always "Bearer" in practice for both GitLab and Atlassian. */ tokenType: string; /** * Absolute expiry time as a Unix timestamp in seconds. * Compare against `Math.floor(Date.now() / 1000)` to detect expiry. */ expiresAt: number; /** OAuth2 scopes granted for this token. */ scopes: string[]; /** * Atlassian cloud site resource ID. * Required for Atlassian API calls; undefined for GitLab tokens. */ cloudId?: string; /** * Base URL of the Atlassian cloud site (e.g. https://myorg.atlassian.net). * Stored alongside cloudId for human-readable diagnostics. * Undefined for GitLab tokens. */ cloudUrl?: string; } /** * Port interface for the encrypted SQLite token store. * * Implemented by `SqliteTokenStore` in `sqlite.ts`. The synchronous method * signatures are intentional — `better-sqlite3` is synchronous by design, * which avoids async overhead for short-lived in-process operations and * keeps the caller-side code free of unnecessary `await` chains. * * Callers that require an async-compatible abstraction should wrap this * interface in a thin async adapter rather than changing the signatures here. */ export interface TokenStore { /** * Retrieve the decrypted token for a service. * * @param service - Service identifier (e.g. 'gitlab', 'atlassian') * @returns The decrypted StoredToken, or null if no token is stored. * @throws StoreError(TOKEN_DECRYPTION_FAILED) when the stored ciphertext * fails GCM authentication — indicates key mismatch or data tampering. */ get(service: string): StoredToken | null; /** * Insert or update the token for a service (upsert semantics). * * Serialises the token to JSON, encrypts it, then writes to the `tokens` * table using INSERT OR REPLACE. The `updated_at` column is refreshed * automatically by the SQL default expression. * * @param service - Service identifier * @param token - Token data to persist * @throws StoreError(DB_WRITE_FAILED) on SQLite write failure. */ upsert(service: string, token: StoredToken): void; /** * Remove the token for a service. * * No-op when no token exists for the given service. * * @param service - Service identifier to remove */ delete(service: string): void; /** * List all services that have a stored token. * * Does not decrypt any token data — reads only the `service` and * `updated_at` columns from the `tokens` table. * * @returns Array of entries, each with the service name and last-updated * timestamp as an ISO 8601 string. */ list(): Array<{ service: string; updatedAt: string; }>; /** * Close the underlying database connection. * * Must be called when the token store is no longer needed (e.g. on daemon * shutdown) to release the file lock and flush WAL frames. After calling * `close()`, any subsequent call to `get`, `upsert`, `delete`, or `list` * is undefined behaviour. */ close(): void; } /** * Port interface for AES-256-GCM encryption. * * Implemented by `createEncryptor` in `encrypt.ts`. The Buffer-in / Buffer-out * contract keeps the interface free of Node.js crypto internals so test doubles * can implement it without the full crypto pipeline. * * Wire format: `nonce (12 bytes) || ciphertext (N bytes) || auth tag (16 bytes)`. * This layout is byte-compatible with the Go implementation in * `padua-mcp/internal/store/encrypt.go`. */ export interface Encryptor { /** * Encrypt plaintext using AES-256-GCM. * * Generates a fresh 12-byte random nonce per call (nonce reuse is a * catastrophic failure for GCM — never pass a nonce as an argument). * * @param plaintext - Raw bytes to encrypt * @returns Combined buffer: nonce (12) || ciphertext (N) || auth tag (16) */ encrypt(plaintext: Buffer): Buffer; /** * Decrypt a combined AES-256-GCM buffer produced by `encrypt`. * * Slices the nonce from bytes 0–11, the auth tag from the final 16 bytes, * and the ciphertext from everything in between. Calls `setAuthTag` before * `final()` so Node.js crypto verifies the GCM authentication tag. * * @param ciphertext - Combined buffer: nonce (12) || ciphertext (N) || tag (16) * @returns Decrypted plaintext bytes * @throws StoreError(TOKEN_DECRYPTION_FAILED) — never a generic Error — * when the GCM authentication tag check fails. Indicates either a * wrong encryption key or data tampering. */ decrypt(ciphertext: Buffer): Buffer; } /** * Result returned by store migration utilities. * * Used by `runMigrations` in `migrate.ts` and by any future migration helper * that moves tokens from a legacy format (e.g. plain-text JSON files) to the * encrypted SQLite store. */ export interface MigrationResult { /** * Whether a migration was actually performed. * `true` when schema changes were applied; `false` when already up to date. */ migrated: boolean; /** * Human-readable explanation of why migration was skipped or what happened. * Present when `migrated` is false and a non-trivial skip condition exists. */ reason?: string; /** * Number of token rows affected or created during the migration. * Populated when a data migration (not just schema migration) was performed. */ tokensCount?: number; } //# sourceMappingURL=types.d.ts.map