# Google Drive home Source: https://selfstore.dev/docs/google-drive Back up a browser app to the user's own Google Drive with one call - store.connectDrive - no backend, and the honest token trade-off. The Drive home stores the encrypted backup in **the user's own Drive**, not yours. You never operate storage, and Google never sees cleartext: the file is encrypted before it leaves the device, so the cloud holds opaque bytes. ## One call ```ts import { gisDriveAuth } from 'selfstore'; const outcome = await store.connectDrive(gisDriveAuth({ clientId: GOOGLE_CLIENT_ID })); // 'started' | 'merged' | 'manual' | 'cancelled' (see quick start) ``` One prerequisite: an OAuth client id from a Google Cloud project (APIs and Services, then Credentials, then "OAuth client ID", type Web application, your origin listed). selfstore requests only the `drive.file` scope, which grants access **solely to files this app created**, the narrowest scope Drive offers. ### The options it takes ```ts interface GisDriveAuthOptions { clientId: string; scope?: string; hint?: string | (() => string | null | undefined); persist?: GisTokenPersistence; // 'memory' (default) | 'session' } ``` `hint` pre-selects an account in the Google chooser - pass a function when your app only learns which account later. `persist: 'session'` keeps the token for the tab so a reload does not re-prompt; `'memory'` drops it, which is the safer default and the one a shared machine wants. `driveTarget.account()` answers **who holds the backup**, as a `DriveAccount { email, name }`. Worth showing next to "saved to Google Drive": a brand name is not an address, and several accounts look alike. If the Drive file is encrypted, pass the password so the merge can read it: `store.connectDrive(auth, { password })`. Without it the call throws `PASSWORD_REQUIRED` before changing anything, so you can prompt and retry. ## The honest trade-off Stated plainly, because it shapes your UX: - **Client-only (`gisDriveAuth`): zero backend, periodic re-consent.** Google Identity Services issues roughly hour-long tokens to pure browser apps, and third-party-cookie rules prevent silent renewal forever. Expect an occasional one-click re-consent. Fine for a backup that syncs a few times a day. - **Permanent connection: a small token broker.** For "connected for good", exchange the OAuth code server-side for a refresh token and mint short-lived access tokens on demand. That is the one place a tiny backend buys real UX, and it still never sees data. `DriveAuth` is three methods, so a broker slots in: ```ts import type { DriveAuth } from 'selfstore'; const brokerAuth: DriveAuth = { async token({ signal } = {}) { /* fetch a fresh access token; pass signal to your fetch */ }, async reconnect() { /* run consent again; true if access re-established */ }, async forget() { /* drop the session, locally and broker-side */ }, }; await store.connectDrive(brokerAuth); ``` Either way, a **genuine** loss of access (a real 401) surfaces as `store.status.action === 'reconnect'`; a cold start or a blip is retried silently and never bothers the user. `token()` also receives a `signal` (since 1.1.0) that fires the moment the user disconnects the backup. A broker running its own retry loop should pass it to its fetches and stop retrying at once, so a "disconnect" tap lands immediately instead of waiting for a slow request to time out. Ignoring the signal keeps working exactly as before - the loop just runs to its own deadline first. ## On the advanced store Driving [`createLocalStore`](https://selfstore.dev/docs/advanced) yourself, the home is `driveTarget` from the advanced subpath, which also ships shared-file primitives (`preview`, `adopt`, `findOrCreateOwnFile`) for pointing several devices at one file: ```ts import { driveTarget, gisDriveAuth } from 'selfstore/advanced'; const drive = () => driveTarget.connect({ auth, kv: cache.kv, fileName: 'my-app.zip' }); await store.attachTarget(await drive(), { strategy: 'merge', password }); ``` ## Sharing with other people Handing a copy to somebody else is a different file, never the backup itself: `createCompanion` mints one next to it, `share` publishes it on a link, `unshare` takes that back, `owner` says whose Drive a copy lives on, and `secondary` is the read-write target a mirror publishes into. The full reference is in [the advanced API](https://selfstore.dev/docs/api-advanced#sharing-over-drive-companion-files); the topology those calls serve is [peers](https://selfstore.dev/docs/peers). One thing they deliberately do not do: **read another account's shared file.** The `drive.file` scope only sees files your app created or the user picked, so reaching somebody else's copy needs either the Google Picker (a gesture per file) or a relay you host - and a relay is a server, which is not a decision a zero-server library gets to make for you. Map of this site for a model: https://selfstore.dev/llms.txt Every page in one file: https://selfstore.dev/llms-full.txt