{"id":21884565,"url":"https://github.com/ozymandiasthegreat/autodrive","last_synced_at":"2025-10-08T21:53:06.001Z","repository":{"id":265010541,"uuid":"843953017","full_name":"OzymandiasTheGreat/autodrive","owner":"OzymandiasTheGreat","description":"Dumb, permissionless, but secure, real-time, multi-writer, distributed file system","archived":false,"fork":false,"pushed_at":"2025-04-09T11:17:44.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-01T20:48:24.790Z","etag":null,"topics":["autobase","distributed-file-system","hyperdrive","multi-writer","p2p"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OzymandiasTheGreat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"OzymandiasTheGreat","patreon":"ozymandias","custom":"https://donorbox.org/tomasrav-open-source-development"}},"created_at":"2024-08-17T23:22:22.000Z","updated_at":"2025-04-09T11:19:48.000Z","dependencies_parsed_at":"2025-07-11T16:26:44.698Z","dependency_job_id":"f9936a69-81ff-4601-b963-f7cdc8ce952e","html_url":"https://github.com/OzymandiasTheGreat/autodrive","commit_stats":null,"previous_names":["ozymandiasthegreat/autodrive"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/OzymandiasTheGreat/autodrive","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OzymandiasTheGreat%2Fautodrive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OzymandiasTheGreat%2Fautodrive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OzymandiasTheGreat%2Fautodrive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OzymandiasTheGreat%2Fautodrive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OzymandiasTheGreat","download_url":"https://codeload.github.com/OzymandiasTheGreat/autodrive/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OzymandiasTheGreat%2Fautodrive/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000693,"owners_count":26082837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autobase","distributed-file-system","hyperdrive","multi-writer","p2p"],"created_at":"2024-11-28T10:14:37.907Z","updated_at":"2025-10-08T21:53:05.970Z","avatar_url":"https://github.com/OzymandiasTheGreat.png","language":"JavaScript","funding_links":["https://github.com/sponsors/OzymandiasTheGreat","https://patreon.com/ozymandias","https://donorbox.org/tomasrav-open-source-development"],"categories":[],"sub_categories":[],"readme":"# autodrive\n\nDumb, permissionless, but secure, real-time, multi-writer, distributed file system\n\n## Install\n\n`npm i autodrive`\n\n## Usage\n\n```javascript\nimport Autodrive from \"autodrive\"\nimport b4a from \"b4a\"\nimport Corestore from \"corestore\"\nimport { once } from \"bare-events\"\nimport fs from \"fs\"\n\nconst driveA = new Autodrive(new Corestore(\"./storageA\"))\nawait driveA.ready()\nconst driveB = new Autodrive(new Corestore(\"./storageB\"), driveA.key)\nawait driveB.ready()\n\nawait driveA.put(\"/example.txt\", b4a.from(\"Hello, World!\"))\nconst data = await driveB.get(\"/example.txt\")\n\nconst ws = driveB.createWriteStream(\"/example.txt\")\nconst rs = fs.createReadStream(\"./file.txt\")\nrs.pipe(ws)\nawait once(ws, \"finish\")\n\nfor await (const chunk of driveA.createReadStream(\"/example.txt\")) {\n  console.log(b4a.toString(chunk))\n}\n\nawait driveA.del(\"/example.txt\")\n```\n\n## API\n\n#### `const drive = new Autodrive(store, key, options)`\n\nCreate new Autodrive. `store` must be an instance of Corestore.\nIf you provide a key you get read access to the drive at key.\nOptions are passed-through to underlying autobase.\n\n#### `await drive.ready()`\n\nWait until internal state is loaded. Use it once before accessing synchronous properties like `key`.\n\n#### `await drive.close()`\n\nFully close this drive and all internal hypercores used by it.\n\n#### `drive.id`\n\nThe ID of the drive, this consists of base-z-32 encoded key of the drive.\n\n#### `drive.key`\n\nThe binary hash of the drive's signers. Giving this someone grants full read access to the drive.\n\n#### `drive.discoveryKey`\n\nHash of the `drive.key`. Use this to e.g. replicate drive over Hyperswarm without leaking read access.\n\n#### `drive.keyPair`\n\nFor writable drives this is the key pair used to authenticate local writer. `null` otherwise.\n\n#### `drive.encrypted`\n\nBoolean indicating whether this drive uses encryption.\n\n#### `drive.encryptionKey`\n\nBuffer containing the key used to encrypt this drive. `null` otherwise.\n\n#### `drive.local`\n\nLocal writer hypercore. If the drive is not writable this is null.\n\n#### `drive.db`\n\nThe underlying file database, an instance of Hyperbee.\n\n#### `drive.store`\n\nThe Corestore used to instantiate this drive.\n\n#### `drive.corestore`\n\nThe Corestore used to instantiate this drive. Hyperdrive compat.\n\n#### `drive.version`\n\nThe current version of the drive's file database.\n\n#### `drive.writable`\n\nBoolean indicating whether this drive has write access.\n\n#### `drive.readable`\n\nShould always be true.\n\n#### `drive.supportsMetadata`\n\nWhether this drive supports file metadata. Should always be true.\n\n#### `const blobs = await drive.getBlobs(forWriter?: Uint8Array)`\n\nGet the Hyperblobs instance for the given writer. If `forWriter` is omitted, returns Hyperblobs for the local writer.\n\n#### `await drive.addWriter(key: Uint8Array, { indexer?: boolean })`\n\nAdd writer to this drive. `key` can be obtained by accessing `drive.local.key` on the writer to be added. Can only be called by existing writer.\n\n#### `await drive.removeWriter(key: Uint8Array)`\n\nRemove a writer from this drive. `key` should be `drive.local.key` of the writer to be removed. Can only be called by existing writer.\n\n#### `await drive.update()`\n\nTry to download the latest version of the files database.\n\n#### `const stream = drive.replicate(isInitiator: boolean)`\n\nSee [`Corestore.replicate()`](https://github.com/holepunchto/corestore#const-stream--storereplicateoptsorstream)\n\n#### `const done = drive.findingPeers()`\n\nIndicating that you're finding peers in the background, operations will be on hold until `done()` is called.\nCall `done()` when current iteration finishes, e.g. after `swarm.flush()`.\n\n#### `const checkout = drive.checkout(version: number)`\n\nCheckout an earlier database version. This allows to go back in time to an earlier filesystem state.\n\n#### `const mirror = drive.mirror(dest: Drive, options?: MirrorDrive Options)`\n\nMirror this drive into an instance of either another Autodrive, Hyperdrive, or Localdrive. Returns an instance of MirrorDrive.\n\n#### `const item = await drive.entry(path: string | Node, options?: { follow?: boolean, wait?: boolean, timeout?: number })`\n\nGet an entry in the files database.\n\n- If `follow` is true and the entry is a symlink, this will resolve the destination.\n- `wait: false` returns immediately, if the entry is not locally available, it will return `null`.\n- `timeout` specifies how long to wait for the entry data to download. The default timeout of 0 means wait indefinitely.\n\n#### `const buffer = await drive.get(path: string | Node, options?: { follow?: boolean, wait?: boolean, timeout?: number })`\n\nGet the file contents. Takes either a path, or a Node object returned by e.g. `drive.entry()`.\nOptions are the same as `drive.entry()`.\n\n#### `await drive.put(path: string, data: Uint8Array, options?: { executable?: boolean, metadata?: JSON })`\n\nWrite data to the filesystem, either creating or updating a file. For large data consider `drive.createWriteStream()`.\n\n#### `await drive.del(path: string)`\n\nDelete a file from filesystem.\n\n#### `for await (const chunk of drive.createReadStream(path: string, options?: { start?: number, end?: number, length?: number, follow?: boolean, wait?: boolean, timeout?: number }))`\n\nGet a readable stream of file data at path. Options are same as `drive.entry()` with the addition of range options:\n\n- `start`: byte offset at which to start reading\n- `end`: byte offset at which to stop\n- `length`: how many bytes to read\n\n#### `const ws = drive.createWriteStream(path: string, options?: { executable?: boolean, metadata?: JSON })`\n\nEfficiently write data at given path, returns a writable stream. Options are the same as `drive.put()`.\n\n#### `await symlink(path: string, dest: string, options?: { executable?: boolean, metadata?: JSON })`\n\nCreate a symlink to another location in the filesystem. Options are the same as `drive.put()`.\n\n#### `await drive.exists(path: string, options?: { wait?: boolean, timeout?: number })`\n\nCheck if the file at given path exists. Options are the same as `drive.entry()` without the `follow`.\n\n#### `for await (const item of drive.list(folder?: string, options?: { recursive?: boolean }))`\n\nList file entries at a given folder. If `recursive: true` also descends into the subdirectories.\n\n#### `for await (const filename of drive.readdir(folder?: string))`\n\nList filenames of entries at given folder.\n\n#### `const int = drive.compare(a: Node, b: Node)`\n\nReturns `0` if entries are the same, `1` if `a` is older, and `-1` if `b` is older.\n\n#### `for await (const { left: Node, right: Node } of drive.diff(version: number, folder?: string, options?: { limit?: number }))`\n\nEfficiently create a stream of the shallow changes to folder between `version` and `drive.version`.\nEach entry is sorted by key.\nIf an entry exists in `drive.version` of the folder but not in `version`, then `left` is set and `right` will be `null`, and vice versa.\n\n#### `const watcher = drive.watch(folder?: string)`\n\nReturns an iterator that listens on `folder` to yield changes, by default on `/`.\n\nUsage example:\n\n```javascript\nfor await (const [current, previous] of watcher) {\n  console.log(current.version)\n  console.log(previous.version)\n}\n```\n\nThose current and previous are Autodrive snapshots that are auto-closed before next value.\nDon't close those snapshots yourself because they're used internally, let them be auto-closed.\n\n##### `await watcher.ready()`\n\nWaits until the watcher is loaded and detecting changes.\n\n##### `await watcher.destroy()`\n\nStops the watcher. You could also stop it by using break in the loop.\n\n##### `await drive.download(folder?: string, options?: { recursive?: boolean })`\n\nDownloads the blobs corresponding to all entries in the drive at paths prefixed with `folder`.\n\n#### `await drive.downloadDiff(version: number, folder?: string, options?: { limit?: number })`\n\nDownloads all the blobs in `folder` corresponding to entries in `drive.checkout(version)` that are not in `drive.version`.\nIn other words, downloads all the blobs added to `folder` up to `version` of the drive.\n\n#### `const { blocks: number } = await drive.clear(path: string | Node, options?: { diff?: boolean })`\n\nDeletes the blob from storage to free up space, but the file structure reference is kept.\nPass `{ diff: true }` to get stats about cleared blocks. Otherwise returns void.\n\n#### `const { blocks: number } = await drive.clearAll(options?: { diff?: boolean })`\n\nClear all blobs from storage, freeing up space. Options are the same as `drive.clear()`\n\n#### `await drive.purge()`\n\nDelete all data used by this drive from the local filesystem, effectively erasing the drive.\n\n## Types\n\n### Blob\n\nThe ID object identifying data store in blobs hypercores,\npaired with a source this allows to retrieve the actual binary data.\n\n```typescript\ninterface Blob {\n  blockOffset: number\n  blockLength: number\n  byteOffset: number\n  /** The size of this file in bytes */\n  byteLength: number\n}\n```\n\n### Entry\n\nThe main file object Autodrive deals with.\n\n```typescript\ninterface Entry {\n  /** The writer core key that produced this version of a file */\n  source: Uint8Array\n  /** Blob object pointing to the actual file data */\n  blob?: Blob | null\n  executable: boolean\n  /** If this is a symlink, linkname will point to the destination */\n  linkname?: string | null\n  /** Any additional data you want to associate with the file, stored as JSON */\n  metadata?: JsonValue\n}\n```\n\n### Node\n\nCarry over from hyperbee, Node wraps entry with additional metadata.\n\n```typescript\ninterface Item {\n  /** The database version when this entry was written */\n  seq: number\n  /** The path at which this entry is stored */\n  key: string\n  /** The Entry object */\n  value: Entry\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozymandiasthegreat%2Fautodrive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fozymandiasthegreat%2Fautodrive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozymandiasthegreat%2Fautodrive/lists"}