Google Drive managed object storage

Review date: 2026-10-12. Status: provisional architecture and security review; this document does not establish implementation completion or live Google conformance. Human reviewer: ___ Date: ___ Outcome: ___. Production release remains disabled. The coordinator records implemented behavior and test evidence in DEVLOG.md.

The authority is the logical mapping

The shared object-storage contract has exact-key reads, conditional writes and deletion, with no public listing. A Drive adapter can implement this contract by storing immutable binary payloads in a dedicated managed Drive folder while the account's durable metadata owns the logical namespace. Application directory/index objects remain ordinary objects; a provider's private mapping is an implementation detail, not a public listing API or an application directory.

Each mapping binds the account, resource and connection generation, exact logical key, fresh opaque logical version, remote file ID, payload SHA-256, byte size and content type. Drive's version is output-only metadata reflecting server changes. It is useful for detecting changes during a read; it is not a write precondition. The documented files.update parameters do not establish an atomic comparison with that version. Never advertise upstream compare-and-swap based on a read/version-check/PATCH sequence. Drive File resource, files.update.

Managed replacement creates a new remote file and atomically changes the local mapping. It never overwrites the old remote payload. A metadata transaction checks the exact expected logical version or absence, checks the current connection generation, and commits the mapping and durable operation result together. That transaction is the conditional write's linearization point. The gateway may serialize work as an optimization; a process-local lock alone is not the persistent authority. Remote network calls must run outside retryable metadata transactions.

Concurrent writers may both upload candidate payloads, but only one mapping transaction can win. A stale writer receives a conflict and its candidate is an orphan pending explicit reconciliation. Fresh versions on every replacement, including replacement with identical bytes, avoid delete/recreate ABA problems. A logical delete conditionally removes the mapping or writes a tombstone in the same authority; an external file's disappearance must never be interpreted as absence of the logical mapping and permit an absent-only overwrite.

This model depends on a single durable mapping authority per account/resource. Multiple devices work through that authority. Another account, deployment or resource must not adopt the same managed namespace by filename, folder ID or a scan and claim coordinated conditional writes. A reconnect that retains mappings must prove the same external principal and namespace; otherwise it creates a fresh resource/generation. Backups and account lifecycle operations must preserve mappings, operation journals and connection generations together. Drive alone cannot reconstruct the authoritative mapping or its versions. Identity lookup sharding does not turn these resource mappings into separate writers.

Remote bytes and failures

Before upload, reserve the mutation in durable metadata with the request ID/digest, logical precondition, connection generation, candidate file ID and expected payload digest. Obtain IDs through files.generateIds, then persist them before files.create. Google documents that retrying a create with a pre-generated ID avoids duplicate files after an indeterminate timeout; an already successful action returns HTTP 409 on retry. A 409 is a signal to fetch and validate the expected candidate, not unconditional proof of success. Binary files and folders support this mechanism. Upload using pre-generated IDs.

After upload, validate the returned ID, parent, binary MIME type, size and bytes/digest before committing. Preserve a pending outcome if a timeout cannot be reconciled. Record candidate IDs before effects so failures do not create unknown orphan files. A crash after the mapping commit must leave a replayable result or sufficient journal evidence to reconstruct it; retrying must not create a second logical mutation. Bound pending records, upload bytes, remote calls, retries and cleanup work.

A read obtains the mapping, checks remote metadata and direct managed-folder membership, downloads bounded bytes, and checks metadata again. Compare the original expected payload digest and size, not merely the two observed Drive versions. Otherwise an external edit completed before the first check could masquerade as the original logical object. Return the local logical version. External rename, move, trash, deletion or edit is an integrity/availability event; it never updates the mapping implicitly. Native documents, shortcuts and arbitrary existing file IDs are outside this managed binary-object path.

Metadata checks before and after a download detect ordinary concurrent changes; they do not create an atomic upstream folder authorization boundary. A changed file may have been fetched before the second check rejects it. The connector uses the already approved managed file ID, returns no unverified bytes, and must disclose that residual race rather than claim Drive enforces folder-scoped credentials.

Logical deletion and physical erasure are different operations. Retaining superseded/candidate payloads is a safe initial implementation only when disclosed, bounded operationally and not reported as remote erasure. A check followed by unconditional Drive deletion cannot protect an external edit/move racing between those calls. Automated physical cleanup needs an explicit managed-file ownership policy and tested semantics; do not imply atomic conditional upstream deletion. Disconnect removes credentials and access; it need not erase the user's Drive files. Report any remote cleanup left incomplete.

OAuth and resource lifecycle

Use the confidential web-server authorization-code flow with an exact configured callback, access_type=offline, and PKCE S256. State is random, short-lived, single-use and stored server-side, bound to the authenticated local account, initiating owner session, connector, connection generation and intended action. Initiation is a CSRF-protected owner mutation. The callback verifies the still-live initiating session/account, then atomically consumes state before exchanging the code. Error callbacks must not accept missing/mismatched state. Never use a browser-provided account/resource ID or callback URL as authority. Keep redirect destinations fixed and remove code/state from the browser address promptly. Google's web-server guide documents offline tokens, state validation, exact redirects and revocation; its PKCE documentation specifies verifier/challenge exchange. Web-server OAuth, Google PKCE parameters.

Request https://www.googleapis.com/auth/drive.file for app-created or specifically selected files. A dedicated newly created folder is a viable baseline with no Picker dependency. If existing file/folder selection is offered, use Google Picker and validate the returned file metadata server-side. A manually pasted folder ID does not grant OAuth access; selection of a folder must not be represented as an unrestricted recursive grant over pre-existing descendants. Avoid broad Drive scopes or silent inclusion of previously granted scopes. Validate returned granted scopes and fail closed when required access is missing. Google recommends drive.file with Picker for narrow file access. Drive scopes, Picker overview.

Encrypt refresh credentials at rest with managed key material; never return them through resource descriptions, callback HTML, logs or browser storage. Reuse the runtime's credential envelope, but bind account/resource/generation inside the authenticated encrypted value and validate those fields on decryption; its existing additional authenticated data is a generic credential label. Limit Google egress to fixed HTTPS endpoints with redirects disabled. Bound and validate token/error response bodies; expose sanitized errors only.

Refresh is serialized or fenced per connection. A refresh response may update only the connection generation that started it. Preserve an existing refresh token only for the same proven Google identity when a response omits one; never carry a token across a newly selected account. Invalid/revoked refresh tokens put the resource into reconnect-required state. A late callback or refresh must not resurrect a disconnected resource. Google OAuth security practices.

Disconnect is deliberately local: atomically remove the selected resource's encrypted credential record and deny new calls through that resource, while leaving other resource connections unchanged. The result explicitly reports {deleted:true, providerRevoked:false, localOnly:true}; Google access-token expiry and already admitted external effects retain their existing limits. Google documents that token revocation invalidates the Google principal's authorization across the entire OAuth project, including its other clients, and that full effect can be delayed. Therefore this resource-removal operation never invokes Google's revocation endpoint. A separate deliberate action through Google Account connection management can revoke that broader authorization; no caller-controlled global revoke is exposed here. Google project-wide revocation semantics.

Evidence required before stronger claims

Local tests should cover concurrent absent/version writes with one winner, independent controller instances sharing durable metadata, stale generations, restart at upload/commit boundaries, unknown-create reconciliation, failed/late refresh after disconnect, state replay/session mismatch, scope denial, modified bytes before and during download, moved/trashed files, logical-delete behavior, and absence of public listing. Runtime integration must exercise both Node persistence and actual Worker metadata transactions. UI changes need DOM/browser verification.

Real-account checks remain separate: configured OAuth redirect and scopes, refresh/revoke behavior, managed-folder creation, pre-generated-ID create/retry behavior, upload/download digests, external edits/moves and quotas. Populated credentials, mocked Google responses and screenshots do not establish these guarantees. Capability language must name locally implemented managed behavior and retain the production/provider-conformance gates in security.md.

Implementation review checkpoint

The first packages/storage/google-drive.mjs implementation, mapping-preserving reconnect and local-only disconnect were independently reviewed with tests/google-drive-audit.test.mjs. The provider and audit suites together passed 36 tests on 2026-10-12; adding the durable Node gateway test passed 37/37. The full npm test run following the disconnect change passed 396/396. Provider responses remain mocked; these checks do not replace real-account conformance.

The review reproduced and prompted fixes for refresh failure resurrecting a disconnected resource, stale refresh success overwriting newer credentials, stale refresh failure disabling a newer successful connection, unauthorized reads/writes revealing object presence through precondition results, missing upstream version acceptance, malformed token-type handling, and missing post-download membership verification. Tests also cover OAuth state owner/session binding, replacement attempts, expiry/replay/denial; granted scope validation; encrypted credential transplant; independent-controller write conflicts; full-body digest validation for ranged reads; unknown upload outcomes; disconnect during upload; fixed HTTPS egress with redirects rejected; and resource/pending-upload limits.

Disconnect regression tests verify that removing one connection makes no Google request, preserves same-principal and unrelated-principal/owner resource records, works while Google is unavailable, and cannot delete a concurrently refreshed credential record from a stale snapshot. There is no automatic project-wide revocation or sibling status update.

Current limitations are explicit. Generated file IDs and encrypted pending folder credentials are retained for ambiguous outcomes, but automatic reconciliation/retry and administrative recovery are not implemented by this module. A file upload commits after a successful matching-ID creation response; it does not independently download the upload before publication. Digests and membership are checked on reads. The surrounding runtime mutation journal must govern request replay and indeterminate responses; the module's upload-pending record is not a complete replay receipt.

All old/deleted payloads remain in Drive and continue consuming Drive storage. The module limits a resource to 10,000 current logical objects and 10,000 retained versions, and limits unfinished uploads and unfinished folder connections to 100 each per account metadata authority. Hitting the retained-version limit can block replacement and deletion until an administrator resolves retained state; there is no automatic compaction. Disconnect does not reclaim those payloads. These limits bound metadata growth under the supported path, rather than proving a byte-budget or remote cleanup guarantee. Every ranged read downloads and verifies the full object, up to one MiB, before slicing the requested bytes.

Initial OAuth completion creates a fresh resource and folder. An explicit reconnect for an existing resource preserves its mapping only after matching the Google about.user.permissionId, checking the original folder's ID, owner, app resource marker, folder type, untrashed state and ability to add children, then atomically advancing the connection generation. Competing reconnects have one winner; disconnect or another generation change prevents publication, and an old-generation upload cannot publish afterward. Google documents about.get as available under drive.file and identifies the user's permission ID in the User resource. about.get, Drive User.

A lost local mapping is not recoverable by signing in again. The implemented OAuth scope is solely drive.file; no Google Picker flow or arbitrary existing-folder import is present. Release notes/UI must retain these distinctions until implemented and independently verified.

The actual Worker HTTP tests in tests/worker/google-drive.test.mjs passed 4/4 with dummy OAuth configuration and real local Durable Objects. They verify authenticated owner/Origin/CSRF/method/field boundaries, encrypted session-bound state and PKCE, inert public 303 callbacks with bounded query-to-fragment conversion and private headers, denied-consent consumption, wrong-session rejection, and disabled behavior without configuration. Miniflare required approved loopback access after the sandbox initially rejected its local listener. These tests deliberately stop before any Google exchange.

The account gateway review also prompted corrections to preserve freshly refreshed credentials during resource verification, enforce canonical base64 before Drive writes, admit owner work before creating OAuth state, record owner activity on successful connection work, and declare Drive authority/retention fields in the shared response schema. The account invocation gate serializes gateway requests; durable mapping transactions supply the conditional publication guard, and existing mutation receipts govern completed/replayed requests. Schema changes and runtime wiring remain provisional under the repository's sign-off process.