Simulators
Google Drive changelog
What changed in each Google Drive simulator release, newest first.
v0.0.8 — 2026-11-05
-
A slow client no longer holds up the others. The simulator reads a request’s whole body before it starts serving it and sends the answer after, so a client that uploads slowly, or reads a large answer slowly, no longer delays other clients’ requests. Answers are unchanged; one larger than 4 MiB still makes other requests wait while it is sent.
-
Shared drives, download operations and batch requests.
drives.create,drives.get,drives.list,drives.update,drives.delete,drives.hideanddrives.unhideserve shared drives:drives.createneeds arequestIdand answers 409teamDriveAlreadyExiststo one the same user already used on a drive that still exists, a drive’s members holdorganizer,fileOrganizer,writer,commenterorreader,drives.get,drives.update,drives.hideanddrives.unhideanswer 404 to a caller who is not a member, only an organizer renames or deletes a drive, and a drive that still holds an untrashed item answers 403cannotDeleteResourceWithChildren.files.createplaces a file in a drive, and a permission on a drive’s id adds a member.POST /drive/v3/files/{fileId}/downloadanswers an operation that is already done, whosedownloadUrifetches the bytes, andGET /drive/v3/operations/{name}reads it back for the user who started it.POST /batch/drive/v3runs each part of amultipart/mixedbody as its own request and answers one response part per part, in order.files.listwithincludeItemsFromAllDrives=trueandchanges.getStartPageTokenwithdriveIdrefuse a missingsupportsAllDrivesas Drive does,changes.listwithdriveIdalso refuses a missingincludeItemsFromAllDrives, and the change routes withdriveIdanswer 404 for an id that names no drive and 403teamDriveMembershipRequiredto a caller who is not a member. -
The call log shows what each call asked for and whether it succeeded. Each
GET /_rystic/callsentry now carriesargs, the call’s query and body arguments with credentials redacted (long values cut, file contents left out), andok, false for an answer that is not 2xx. A path the simulator does not serve is markedunknown_route: true.?tail=Nreturns only the last N calls, anddroppedcounts the calls the log no longer keeps. -
Coverage lists resumable uploads. The docs’ Coverage section now lists the resumable upload session routes under
/upload/drive/v3/filesand/upload/drive/v3/files/{fileId}, which the simulator already served, and counts them in its route total. -
The docs are one page, with a page per feature. The archive ships
README.mdin place ofHOWTO.md, as the standard simulator page, andCHANGELOG.mdbeside it. The change feed and shared drives each have their own page underdocs/features/, as do uploads and downloads, and every example on these pages runs as written against a fresh simulator. -
The docs page links the admin token section. The page now sends you to the admin token’s section of the shared-simulators page.
-
Domain-admin access and shared-drive roles.
useDomainAdminAccess=trueis served ondrives.get,drives.list,drives.updateanddrives.deleteand onpermissions.list,permissions.create,permissions.get,permissions.updateandpermissions.deletefor a shared drive: it opens the drive and its member list to a domain administrator who is not a member, and never the drive’s contents, so the administrator’sfiles.createor upload into the drive answers 404notFoundand stores nothing. A caller who is not an administrator is refused or answered 404, as Drive does, andallowItemDeletion=truewith the flag deletes a drive that still holds items. A seeded user markeddomainAdminis an administrator, and a new drive is visible to it at once, where Drive can take up to about 10 seconds. Any member of a shared drive can read one member’s permission withpermissions.geton the drive’s id. A multipart upload into a shared drive, or into a folder inside one, now stores the file as one of the drive’s items, where the simulator answered 500 or left it outside the drive. In a shared drive, trash, move and share follow the caller’s role: areaderand acommenterare refused with 403insufficientFilePermissions, awritercan share but neither move nor trash, and afileOrganizerororganizercan do all three. As on Drive,files.copywithoutsupportsAllDrives=trueanswers 404 on a file in a shared drive andfiles.listwithoutincludeItemsFromAllDrives=trueleaves shared-drive files out. Right after a grant on a shared drive, Drive can refuse the new member’scomments.createwith 403insufficientFilePermissionsfor up to about two minutes, while the simulator accepts it at once, so retry that 403 with backoff. -
Scenario descriptions use customer terms. The Scenarios table and
--list-scenariosdescribe each scenario in American spelling, with no internal references.
v0.0.7 — 2026-10-31
- Seed large files without inlining their bytes. A seeded revision can name its bytes as a file under the folder
RYSTIC_CONTENT_ROOTnames,"content_ref": "file:<path>", in place ofbody_b64; downloads stream that file from disk without loading it into memory, byte ranges included, and a copy points at the same file instead of duplicating it. A seed naming a file that does not exist is refused.
v0.0.6 — 2026-10-27
- Swagger UI to read and call the twin from the browser.
/_rystic/swagger, linked from the state UI header, is Swagger UI over the published API trimmed to the operations this twin serves; Try it out sends each request to the twin.
v0.0.5 — 2026-10-14
- This version carries every change listed under v0.0.4. v0.0.4 was withdrawn minutes after it shipped, when a check of its downloads hit a transient error, so no customer kept it. This version ships the same twin, including the change feed (
changes.getStartPageTokenandchanges.list).
v0.0.4 — 2026-10-14
-
Resumable uploads.
uploadType=resumableonPOST /upload/drive/v3/filesand on thePATCHof an existing file answers aLocationsession URI, takes the bytes in chunks underContent-Range(308 with aRangeheader until the last one) or in onePUT, answers a status query, and creates or updates the file when the last byte arrives, so a Google client that switches to a resumable upload for a large file works. APOSTwith nouploadTypeis a media upload, as on Drive, and an unknownuploadTypeanswers 400. A multipart upload takes itsmimeTypefrom the metadata, else the media part, else the name’s extension, and stores a nameless file asUntitled. -
A bearer token must look like a Google access token. The twin now accepts a bearer value only when it starts
ya29., the prefix every Google access token carries, and refuses any other as Drive refuses an invalid token: 401authErrorwithWWW-Authenticate: Bearer … error="invalid_token". A client that sent a made-up token such astestmust send one that startsya29., such asya29.placeholder. -
More of Drive’s refusals, as Drive words them. A reader or commenter is refused a rename, an upload, a comment or reply edit, a reply or permission delete and a file in someone else’s folder; a non-owner is refused a delete; a copy or download of a
copyRequiresWriterPermissionfile by a reader or commenter answers 403cannotCopyFileorcannotDownloadFile; a read-only-locked file refuses a rename and a comment;sizeor another read-only member in an update answers 403fieldNotWritable; a folder moved into itself or its own child answers 400; a plain file named as a parent answers 403parentNotAFolder.addParentswithoutremoveParentsmoves the file, amimeTypein an update is ignored, and a download ignoresfields, as on Drive. Each answer was measured on Drive. -
An upload over 64 MiB names the cap. A media or multipart upload over 64 MiB answers 413
request body exceeds blob_cap_bytes 67108864, the cap/_rystic/versionreports, where it answeredrequest body too large. -
A Google Doc’s revision reads and updates as on Drive. A Doc lists one revision, ID
1, with its tenexportLinksand nomd5Checksum,sizeorkeepForever. Updating it answers 200, and deleting it answers 403revisionDeletionNotSupported. Before, the list named a revision no other call could find, and a read or update of it answered 404. -
A deleted reply is scrubbed, and a plain reply reopens a resolved comment. Reading a deleted reply with
includeDeleted=trueanswers onlykind,id,createdTime,modifiedTime,htmlContent"" anddeleted, and itsmodifiedTimeis when it was deleted. A reply without anactionon a resolved comment carriesaction: "reopen"and reopens the comment, as on Drive. -
Docs and folders you create carry Drive’s links and size. A Google Doc made by
files.createhassize“1024”, its tenexportLinksand awebViewLinkondocs.google.com/document/d/<id>/edit. A folder’swebViewLinkisdrive.google.com/drive/folders/<id>. The seeded Docs list all ten export formats. -
A change feed to poll.
GET /drive/v3/changes/startPageTokenandGET /drive/v3/changesserve Drive’s change feed: take a start token before listing withfiles.list, poll from it, read on withnextPageTokenwhile a page carries one, and keep the last page’snewStartPageTokenfor the next poll (pageSize1 to 1000). Each file created, updated, uploaded to, moved, trashed, shared or unshared since the token is listed once, holding its state as the caller reads it now, and one the caller can no longer read is listedremoved: true; each user’s feed holds only what that user can read. The twin lists a change at once, where Drive lists it 1 to 4 s later, andPOST /_rystic/resetrestarts its tokens.changes.watch,files.watchandchannels.stopare not served. The feed’s known gaps, among themfields, the shared-drive parameters and a folder’s trash, are listed with their tickets in the docs. -
The change feed lists what Drive lists. A file someone stops sharing with you, or deletes, is now listed
removed: truein your feed even if you found it throughfiles.listorfiles.getrather than the feed. A token’s answer no longer depends on which polls came before it: reading it again answers the same while nothing has changed since, and picks up later writes in their current state. A file you reach only through a link is never listed. Trashing or untrashing a folder lists every file below it.fieldsnow narrows both changes routes: with nofields, each listed file holds onlykind,id,nameandmimeType; an emptyfieldsanswers{}; and a member a change lacks answers 400invalidParameter. A new comment, a revision kept forever and a deleted revision now list their file, while a reply, even one that resolves its comment, lists nothing. The feed refusesdriveId,includeItemsFromAllDrivesandspacesas Drive does,restrictToMyDrive=truelists only the files you own, and a request with no credential namesDriveChanges.ListorDriveChanges.GetStartPageTokenin its 401.
v0.0.3 — 2026-10-13
-
Seeded files and copies download their bytes, and a file carries only the members Drive gives it. Every binary file in a shipped world downloads the bytes its
sizeandmd5Checksumdescribe (sizes are now tens of kilobytes), and afiles.copydownloads its source’s bytes under its own head revision andcreatedTime. A file no longer answers"",{}ornullfor a member Drive leaves out, so the Go client decodes every world’s file list; a file made from metadata alone carriessize: "0",md5Checksum,headRevisionIdand its links, owners carry an email, and trashing a My Drive file sets notrashedTime. -
A
files.listpage walk survives deletes, and no listing answers the root. ApageTokenkeeps working after the files of an earlier page are deleted or trashed: the walk continues with the files still after it, as Drive’s does. Each world’sMy Driveroot folder is no longer a row offiles.list;files.geton its ID still answers it. -
Ownership transfer works as Drive’s. An owner’s
permissions.createwithtransferOwnership=truemakes the grantee the owner at once and leaves the old owner a writer, soowners,ownedByMeandcapabilitiesfollow; a writer’s transfer answers 400invalidSharingRequest“Sorry, you do not have permission to share.” -
A read-only content restriction locks a file’s content. A file whose
contentRestrictionsholdreadOnly: trueanswerscanModifyContent,canRenameandcanCommentfalse for every caller (canEditstays true, as on Drive), a media update of it answers 403contentRestrictionViolation, andfiles.updatestores the restriction with itstype,restrictingUserandrestrictionTime. The Security Policy doc ingoogle-drive_team_workspacenow agrees with its restriction. -
Folders behave as a tree.
files.updatewithaddParentsandremoveParentsmoves a file, and'<folderId>' in parentsfinds it under its new folder. A file inside a trashed folder readstrashed: truewithexplicitlyTrashed: false, drops out ofq=trashed = false, and comes back when the folder is restored. The IDrootnames yourMy Driveroot on every route that takes a file ID, and a file created withoutparents, byfiles.createor either upload type, lands in that folder under its real ID. -
files.listtakes more of Drive’s search terms.qnow takesfullText contains(whole words of the name, the description or a text file’s content),sharedWithMe,'me' in readersandin writers(a role at or above it, inherited through folders), an email or permission ID inowners,createdTime,modifiedTime <,!=ontrashedandstarred,appProperties has, double-quoted values,True/Falsein any case, andname containswords in any order. WithoutorderBy, or with an empty one, files come newestmodifiedTimefirst, and an empty sort key is skipped.q=name < 'x'still answers 400, where Drive answers 500. -
An upload holds up to 64 MiB. A media or multipart upload, create or update, now stores and downloads byte for byte up to a 64 MiB request body, where it stopped at 1 MiB; a multipart body’s metadata and part headers count toward it. A larger body answers 413
request body too large, and/_rystic/versionreports the cap asblob_cap_bytes. -
'root' in parentslists My Drive’s top level.files.listreadsrootinq’sparentsterm as the caller’s My Drive root, as every route that takes a file ID already does, soq='root' in parentsanswers the files and folders directly under it. -
Every user has a My Drive root.
rootresolves for every user in every world, afterPOST /_rystic/resettoo, and a file or folder created with no parents lands in it. A folder you create no longer becomes the root and drops out offiles.list. -
A media upload onto a folder leaves it a folder.
PATCH /upload/drive/v3/files/{folder}?uploadType=mediaanswers 200 with the folder’s metadata, as Drive does. The twin writes nothing, where it turned the folder into a file; what Drive does with the uploaded bytes is unmeasured. -
JSON answers carry Drive’s charset. Every JSON answer is labeled
Content-Type: application/json; charset=UTF-8, as Drive labels it, where it was bareapplication/json.
v0.0.2 — 2026-10-13
-
A comment’s author carries a name and photo, and
resolvedappears only once a reply acts.- Every comment’s and reply’s
authorcarriesdisplayNameand a protocol-relativephotoLink(//lh3.googleusercontent.com/...) from the author’s user record, withmetrue only for the caller who wrote it. - The seeded users carry a
displayNameand aphotoLink, so a seeded caller’s comments render a full author. - A comment answers no
resolveduntil a reply withactionresolveorreopenacts on it, thentrueorfalse; a deleted comment read withincludeDeleted=truecarries none.
Before, an author whose user record lacked a name or photo answered without them, and every comment answered
resolved: false. - Every comment’s and reply’s
-
A request with no credential answers what Drive answers on that method.
- Most methods answer 401 reason
required, message “Login Required.”, statusUNAUTHENTICATED, with onegoogle.rpc.ErrorInfodetail: reasonCREDENTIALS_MISSING, servicedrive.googleapis.comand the method’s own name, such asgoogle.apps.drive.v3.DriveFiles.Create. Revision downloads (revisions.getwithalt=media) are among them. files.list,files.getand every comments and replies method answer 403forbidden, statusPERMISSION_DENIED, “Method doesn’t allow unregistered callers …”.files.getwithalt=mediaandfiles.exportanswer 403forbidden“The request is missing a valid API key.”files.exportchecks its requiredmimeTypefirst.- An empty bearer value or another scheme counts as no credential. The check runs before the file lookup, so an unknown ID still answers the credential refusal.
Before, most methods answered 401
authError“Invalid Credentials”, and some carried a placeholderexample.googleapis.comdetail.A bearer that is present is still accepted whatever its value: the twin does not check token validity, where Drive answers a bogus token 401
authError“Invalid Credentials”. - Most methods answer 401 reason
-
permissions.listorders entries as Drive does.anyonepermissions come first, thendomainpermissions, then user permissions, where the owner’s entry leads the other user grants, which follow in the order they were made. WithpageSize=1on a file shared by link, the first page is theanyonepermission andnextPageTokencontinues to the owner.Before, the owner’s entry always came first.
-
revisions.listrefuses an unknownpageToken, and two comment routes check the credential first.revisions.listwith apageTokenit never issued answers 400 reasoninvalid, message “Invalid Value”, at locationpageToken, as Drive does. An out-of-rangepageSizekeeps its own 400invalidParameteranswer.comments.updateandreplies.createwith no credential answer Drive’s 403 before any other check.
Before, an unknown
pageTokenanswered thepageSizerefusal, and those two routes answered 401 to a missing credential. -
A multipart upload’s base64 media part is stored as its decoded bytes. An
uploadType=multipartupload whose media part carriesContent-Transfer-Encoding: base64now stores, sizes and checksums the decoded bytes, soalt=mediaanswers the original content; before, the twin stored the base64 text. Base64 that does not decode answers 400parseError, as any malformed multipart body does. -
A
fieldsselector naming a member the resource lacks answers 400, andcomments.listignores an unknownpageToken.- A
fieldsselector naming a member the method’s resource does not have, at any depth (nope,id,nope,owners(nope),capabilities/nope,ID), answers 400invalidParameter“Invalid field selection nope” at locationfields, as Drive does. A create refused this way still writes the file. comments.listwith apageTokenit never issued answers 200 with the first page, as Drive does.
Before, such a selector answered 2xx with nothing for that member, and
comments.listrefused the token with 400invalidParameter. - A
-
files.updateaccepts a member of the wrong type, and an upload refused for itsfieldswrites nothing.files.updatewith a member of the wrong type answers 200 as Drive does:- a non-string
name, such as{…}, leaves the name unchanged; - a non-boolean
trashed, such as"x", reads asfalseand restores a trashed file; - any other wrong-typed member is ignored.
- a non-string
- A body that is not valid JSON still answers 400
parseError. - A media or multipart upload, create or update, whose
fieldsnames a member the File resource lacks answers 400 and writes nothing: no file is created, and an updated file keeps its bytes, as Drive does.
Before, the twin answered
files.update400invalidParameterfor a wrong-typed member, and wrote the refused upload.
v0.0.1 — 2026-10-13
-
A local Google Drive for your integration and the agents that drive it. The first release of the Google Drive twin: 35 operations of the Drive API v3 — one user’s files and folders, uploads and downloads,
files.listwithq,orderByand page tokens, revisions, exports, permissions, and comments and replies — served on one port forwww.googleapis.com’s/drive/v3and/upload/drive/v3routes, from a model validated against live Google Drive. Point Google’s Node or Python client at it, or curl it with any bearer token (HOWTO.mdshows how each client’s uploads reach the twin, since an upload left unpointed goes to the real Google Drive); nothing reaches Google and no real Drive changes. -
Files and folders, created, read, trashed and deleted.
POST /drive/v3/filescreates a file, or a folder withmimeTypeapplication/vnd.google-apps.folder, under the folders itsparentsname;files.get,files.update,files.copyandfiles.deleteread, change, copy and delete it, and every file a shipped world seeds reads back.PATCHwithtrashedmoves a file to the bin and back, andDELETE /drive/v3/files/trashempties the bin. A create naming an IDfiles.generateIdsissued keeps it, an ID a file already holds answers 409fileIdInUseand changes nothing, and an IDgenerateIdsnever issued answers 400fileIdNotUsable. A missing or deleted file answers 404notFoundin Drive’s JSON error envelope. -
Uploads, downloads, revisions and exports.
uploadType=mediaanduploadType=multipartcreate and update a file’s bytes on/upload/drive/v3, as the official clients send them, andalt=mediareads them back: aRangeanswers 206, and one past the end 416 withContent-Range: bytes */<size>and Drive’srequestedRangeNotSatisfiableerror. Each upload records a revision, whichrevisions.list,get,update,deleteandalt=mediareach; a file created from metadata alone lists its head revision, as Drive does.files.exportconverts a Docs, Sheets or Slides file, and withoutmimeTypeanswers 400required“Required parameter: mimeType”. A resumable upload answers 501. -
files.listsearches byq, sorts byorderBy, andfieldsselects the answer.qtakes Drive’s grammar overname(=,!=, andcontainsmatching the start of a word),mimeType,'<folder>' in parents,trashed,starred,modifiedTime,'me' in ownersandproperties has, joined byand,or,notand parentheses;orderBysorts byname,name_natural,folder,createdTime,modifiedTimeand the other documented keys, each withdesc; apageSizeoutside 1 to 1000 answers 400invalidParameter. Afieldsselector narrows every 2xx answer to the members it names,*for the whole resource, and leaves an error whole; without one a route answers Drive’s default members.about.getand every comments and replies method but delete answer 400requiredwithout a selector, as Drive does. -
A caller’s access comes from its role on the file. A caller’s role is owner, or else the highest role a permission on the file or on a folder above it grants, an
anyonelink included: a file the caller holds no role on answers 404notFoundand is left out offiles.list, a reader or commenter gets 403insufficientFilePermissionson an update,permissions.listand a share, and only the owner deletes, trashes or restores.ownedByMe,capabilities,sharedandpermissionIdsare worked out for each caller, andX-Rystic-Actornames which of a world’s users is calling. Only a comment’s or reply’s author changes or deletes it. Drive’s refusals of a query parameter answer as Drive does:useDomainAdminAccess=trueon the permissions routes 404,sendNotificationEmail=trueon ananyonegrant 403, roleownerwithouttransferOwnership403,includeItemsFromAllDrives=truewithoutsupportsAllDrives403supportsTeamDrivesRequired, andacknowledgeAbuse=trueon a metadata read 403invalidAbuseAcknowledgment. -
Shipped Drives to boot straight into.
--scenario google-drive_media_vaultboots Juno Park’s folders of images, video and proofs, shared with a collaborator, the worldcheck.shand the quickstart use.google-drive_shared_inboxholds files two other people shared with Noor Haddad,google-drive_trash_backloga full bin beside one live doc,google-drive_team_workspacea team tree Dana Okafor owns and shares with a writer and a commenter, whose roles each answer as Drive’s do, andgoogle-drive_fresh_mydrivean empty Drive;SCENARIOS.mdsays what each is for. In each, the default caller is the world’s own user, who owns what the world says they own, andabout.getanswers that user’s whole About.POST /_rystic/seedlands users, files, revisions with their bytes, permissions and comments directly, andPOST /_rystic/resetrewinds the sequences and the clock, so two identical runs give byte-identical files. -
Measured against live Google Drive. The full-suite round of 2026-10-12 (
20261012T222054Z) matched 271 of 308 probes against live Google Drive, none inconclusive.HOWTO.mdstates the differences this release ships with: seeded files and copies download 0 bytes,files.listanswers each world’sMy Driveroot as a row, ownership does not move ontransferOwnership, a request with no credential answers 401 where Drive answers 403 on a read, someqforms Drive takes are refused, a read-only file still answerscanModifyContent: true, and some differences in the wire format.