Skip to main content
Glama
README.md
<div align="center">
  <img src="web/public/assets/logo.png" alt="Disk" width="96" height="96" />

  # Disk

  **Personal cloud storage on Cloudflare — for you and your agents.**

  Any file, any size · scoped API keys · full audit trail · agent-ready
  (REST + OpenAPI + MCP)

  [**disk.zaynjarvis.com**](https://disk.zaynjarvis.com) · built on Cloudflare
  Workers · R2 · D1

</div>

---

<div align="center">
  <img src="docs/screenshots/files.png" alt="Disk file browser" width="820" />
</div>

## What it is

Disk is one Cloudflare Worker in front of **R2** (file bytes), **D1** (keys,
shares, audit), and a **React** SPA. It's a Dropbox-lite you own outright —
and every capability is equally available over a clean API so your agents can
use it as scratch space, artifact storage, or a shared drive.

## Features

- 🔑 **API-key auth with roles** — a `ROOT_KEY` env secret, revocable
  root-equivalent **admin** keys, and scoped **user** keys (path prefix,
  read-only/read-write, per-file upload cap, expiry).
- 🗂️ **Virtual root per key** — a scoped key sees its prefix *as* the root and
  never perceives or escapes it. Hand an agent a key scoped to `agents/zouk/`
  and, to that agent, its world simply starts at `/`.
- 📤 **Any file, any size** — drag-and-drop and folder uploads, multipart for
  multi-GB files, Range downloads, previews (image / video / audio / PDF /
  markdown / code).
- 👀 **Four ways to browse** — List, Grid, **Tree**, and a Finder-style
  **Column** view that previews the selected file in a pane on the right,
  making full use of wide screens.
- 🔗 **Shares** — revocable, expiring public links (`/s/<token>`), download-counted.
- 📜 **Full audit** — every request logged (actor, action, path, bytes, status,
  IP, UA); filterable UI + CSV export for admins.
- 🤖 **Agent-ready** — REST at `/api/v1`, OpenAPI 3.1 at `/openapi.json`, an
  agent guide at `/llms.txt`, and an **MCP server** at `POST /mcp` — including
  admin tools so an agent can manage keys itself.

## Screenshots

| Sign in | Preview anything |
|---|---|
| <img src="docs/screenshots/login.png" width="400" /> | <img src="docs/screenshots/preview.png" width="400" /> |

| API keys — roles, scopes, upload caps | Storage usage |
|---|---|
| <img src="docs/screenshots/keys.png" width="400" /> | <img src="docs/screenshots/usage.png" width="400" /> |

**Column view** — Finder-style Miller columns with a live preview pane on the
right; and a **Tree** view for a bird's-eye look. Different views for different
audiences.

| Column view + preview pane | Tree view |
|---|---|
| <img src="docs/screenshots/column.png" width="400" /> | <img src="docs/screenshots/tree.png" width="400" /> |

**Virtual root** — the same disk, seen through a key scoped to `agents/zouk/`.
No prefix, no other folders, no admin views: its scope *is* the root.

<div align="center">
  <img src="docs/screenshots/scoped.png" alt="Scoped key virtual root" width="700" />
</div>

## How to use it

### As a person

1. Open [disk.zaynjarvis.com](https://disk.zaynjarvis.com).
2. Paste an API key (your `ROOT_KEY`, or any key you minted) and sign in.
3. Drag files in, preview them, share links, and — as root/admin — mint keys
   and read the audit log.

### As an agent

Mint a key for the agent in the **Keys** tab (or via the API), then point it at
Disk. Give it a **scoped** key so it can only touch its own corner:

```sh
# (admin) mint a scoped read-write key with a 10 MB per-file cap
curl -s -X POST https://disk.zaynjarvis.com/api/v1/keys \
  -H "Authorization: Bearer $ROOT_KEY" -H 'Content-Type: application/json' \
  -d '{"name":"zouk-agent","prefix":"agents/zouk/","permission":"rw","maxUploadBytes":10485760}'
# → returns {"key":"dk_…"}  (shown once)
```

The agent then speaks **scope-relative** paths — it never sees `agents/zouk/`:

```sh
export DISK_KEY=dk_...
# upload  (stored at agents/zouk/notes/todo.md, but the agent just says "notes/todo.md")
curl -s -X PUT "https://disk.zaynjarvis.com/api/v1/files/blob?path=notes/todo.md" \
  -H "Authorization: Bearer $DISK_KEY" --data-binary @todo.md
# list its root
curl -s "https://disk.zaynjarvis.com/api/v1/files" -H "Authorization: Bearer $DISK_KEY"
# share a file
curl -s -X POST https://disk.zaynjarvis.com/api/v1/shares \
  -H "Authorization: Bearer $DISK_KEY" -H 'Content-Type: application/json' \
  -d '{"path":"notes/todo.md","ttlSeconds":3600}'
```

Full curl reference for every operation: **[/llms.txt](https://disk.zaynjarvis.com/llms.txt)**.

### Over MCP

Connect any MCP client (Claude Code, Claude Desktop, Cursor, …). The agent gets
native tools — `disk_list`, `disk_read`, `disk_write`, `disk_delete`,
`disk_move`, `disk_share`, and (for admin keys) `disk_key_create` /
`disk_key_list` / `disk_key_revoke`:

```json
{ "mcpServers": { "disk": {
  "type": "http",
  "url": "https://disk.zaynjarvis.com/mcp",
  "headers": { "Authorization": "Bearer dk_..." }
} } }
```

Hand an agent an **admin** key and it can administer the whole disk — including
minting and revoking other keys — entirely through MCP.

## Stack

Hono on Cloudflare Workers · R2 · D1 · React 19 + Vite + Tailwind v4 · vitest
(`@cloudflare/vitest-pool-workers`). One `wrangler deploy` ships the API, the
MCP server, and the SPA together.

## Contributing & deploying

- **[DEV.md](DEV.md)** — local setup, the contract-first rule, security-core
  invariants, recipes, and gotchas.
- **[DEPLOY.md](DEPLOY.md)** — architecture, provisioned resources, redeploy,
  migrations, disaster-recovery bootstrap.
- **[API.md](API.md)** — the binding API contract.

Screenshots above are generated with `node scripts/screenshots.mjs` against a
local dev server.

Maintenance

ActivityStale
ResponsivenessNo issues