# Overview

> What vcup exposes (CLI upload/delete, proxy file serving), runtime assumptions (Bun CLI, Vercel serverless + Blob), and the shortest path from install to a shareable link.

- Repository: MaxLeiter/vcup
- GitHub: https://github.com/MaxLeiter/vcup
- Human docs: https://grok-wiki.com/public/docs/maxleiter-vcup-05c16ae77ebb
- Complete Markdown: https://grok-wiki.com/public/docs/maxleiter-vcup-05c16ae77ebb/llms-full.txt

## Source Files

- `README.md`
- `package.json`
- `cli.ts`
- `vercel.json`
- `api/upload.ts`
- `api/f.ts`

---

---
title: "Overview"
description: "What vcup exposes (CLI upload/delete, proxy file serving), runtime assumptions (Bun CLI, Vercel serverless + Blob), and the shortest path from install to a shareable link."
---

**vcup** (`@maxleiter/vcup`) is a Bun-driven CLI plus three Vercel serverless handlers that store files in Vercel Blob, return a proxy URL under `/f/:slug`, and stream those objects back with inline `Content-Disposition` for browser viewing. Upload and delete require a shared `VCUP_TOKEN`; the proxy route is unauthenticated.

## What the project exposes

| Surface | Entry | Role |
| --- | --- | --- |
| CLI | `vcup` → `cli.ts` | Upload files or stdin; optional `--raw`; delete via `vcup rm` |
| Upload API | `POST /api/upload` | Bearer auth, streams body to `@vercel/blob` `put`, returns JSON `url` + `raw` |
| Proxy API | `GET /f/:slug` → `/api/f` | Public fetch from `BLOB_STORE_URL`, MIME sniff, inline serve |
| Delete API | `DELETE /api/delete` | Bearer auth, `del` on blob URL from `X-Blob-Url` |

The published npm package wires the binary directly to the TypeScript entry:

```json
"bin": { "vcup": "./cli.ts" }
```

The CLI shebang (`#!/usr/bin/env bun`) and streaming upload (`duplex: "half"`) assume **Bun** at runtime, not Node for the client.

## Runtime stack

```mermaid
flowchart TB
  subgraph client["Client machine"]
    CLI["cli.ts / vcup"]
    RC["~/.vcuprc or VCUP_API_URL + VCUP_TOKEN"]
    CLI --> RC
  end

  subgraph vercel["Vercel deployment"]
    RW["vercel.json rewrite /f/:slug* → /api/f"]
    UP["api/upload.ts"]
    F["api/f.ts"]
    DEL["api/delete.ts"]
    RW --> F
  end

  subgraph storage["Vercel Blob"]
    BLOB["public blobs + random suffix"]
  end

  CLI -->|"POST + Bearer + X-Filename"| UP
  CLI -->|"DELETE + Bearer + X-Blob-Url"| DEL
  Browser["Browser / curl"] -->|"GET /f/..."| RW
  UP --> BLOB
  DEL --> BLOB
  F -->|"fetch BLOB_STORE_URL/slug"| BLOB
```

| Layer | Technology | Notes |
| --- | --- | --- |
| CLI | Bun | Config from env or `~/.vcuprc`; progress bar on stderr |
| API runtime | Vercel serverless | Node-style `IncomingMessage` / `ServerResponse` handlers |
| Object store | `@vercel/blob` | `put` with `access: "public"` and `addRandomSuffix: true`; `del` on delete |
| Routing | `vercel.json` | Rewrites `/f/:slug*` to `/api/f` |
| Local dev | `vercel dev` | `package.json` script `"dev": "vercel dev"` |

Server handlers disable the default body parser (`export const config = { api: { bodyParser: false } }`) so upload can stream the request body into Blob.

## Upload response shape

After a successful upload, the API returns JSON with two URLs:

<ResponseField name="url" type="string">
Proxy URL: `{baseUrl}/f/{slug}` where `slug` is the blob pathname (random suffix included). `baseUrl` is `VCUP_BASE_URL` or derived from `x-forwarded-proto` and `host`.
</ResponseField>

<ResponseField name="raw" type="string">
Direct Vercel Blob public URL returned by `put`.
</ResponseField>

The CLI prints `url` by default, or `raw` when `--raw` is passed.

## Proxy behavior (public)

`vercel.json` maps browser-facing paths to the proxy handler:

```json
{ "source": "/f/:slug*", "destination": "/api/f" }
```

`api/f.ts` rebuilds `blobUrl = ${BLOB_STORE_URL}/${slug}`, fetches upstream, sets `Content-Type` via `mime-types`, forces `Content-Disposition: inline`, and streams the body. No `VCUP_TOKEN` check on this route.

<Warning>
`BLOB_STORE_URL` must be set on the deployment or proxy requests return **500** with `BLOB_STORE_URL not configured`.
</Warning>

## Authentication boundary

| Route | Auth |
| --- | --- |
| `POST /api/upload` | `Authorization: Bearer {VCUP_TOKEN}` must match server `VCUP_TOKEN` |
| `DELETE /api/delete` | Same Bearer check |
| `GET /f/:slug` (via `/api/f`) | None |

Client token comes from `VCUP_TOKEN` / `~/.vcuprc` `token`, or `VCUP_API_URL` / `~/.vcuprc` `url` for the API host.

## Shortest path: install → shareable link

<Steps>
<Step title="Deploy or use an existing instance">
Fork/deploy via the README Vercel button (provisions Blob store env vars). Set `VCUP_TOKEN`, `BLOB_STORE_URL`, and `BLOB_READ_WRITE_TOKEN` on the project.
</Step>

<Step title="Install the CLI">
<CodeGroup>
```bash title="bun"
bun install -g @maxleiter/vcup
```
```bash title="npm"
npm install -g @maxleiter/vcup
```
</CodeGroup>
</Step>

<Step title="Point the CLI at your deployment">
Create `~/.vcuprc`:

```json
{
  "url": "https://your-vcup-deployment.vercel.app",
  "token": "your-vcup-token"
}
```

Or export `VCUP_API_URL` and `VCUP_TOKEN` (both required if using env-only config).
</Step>

<Step title="Upload and copy the printed URL">
```bash
vcup screenshot.png
```

On success the CLI prints the proxy URL (e.g. `https://your-deployment.vercel.app/f/...`). Open it in a browser to verify inline rendering.
</Step>
</Steps>

Stdin uploads work the same way without a file argument; the default filename is `paste.txt`:

```bash
echo "hello world" | vcup
```

## CLI commands at a glance

| Command | Behavior |
| --- | --- |
| `vcup <file>` | Upload file; stderr progress bar; stdout proxy URL |
| `echo … \| vcup` | Upload stdin as `paste.txt` |
| `vcup --raw <file>` | Print `raw` blob URL instead of proxy `url` |
| `vcup rm <url>` | `DELETE /api/delete` with proxy or raw URL |
| `vcup --help` | Usage and config hint |

Delete via proxy URL requires `BLOB_STORE_URL` in the **client** environment to resolve `/f/` slugs to a blob URL; otherwise pass the raw URL from upload.

## Server environment (deployment)

| Variable | Used by |
| --- | --- |
| `BLOB_READ_WRITE_TOKEN` | Vercel Blob SDK (implicit via platform) |
| `BLOB_STORE_URL` | `api/f.ts` upstream fetch; CLI proxy delete resolution |
| `VCUP_TOKEN` | `api/upload.ts`, `api/delete.ts` |
| `VCUP_BASE_URL` | Optional override for proxy URLs in upload JSON |

See `.env.example` for the minimal server template.

## Repository layout

:::files
vcup/
├── cli.ts          # Bun CLI (upload, rm, config)
├── package.json    # @maxleiter/vcup, bin → cli.ts
├── vercel.json     # /f → /api/f rewrite
└── api/
    ├── upload.ts   # POST, Blob put, JSON { url, raw }
    ├── f.ts        # GET proxy, inline stream
    └── delete.ts   # DELETE, Blob del
:::

<Info>
There is no separate web UI—sharing is entirely CLI upload plus HTTP GET on the proxy path.
</Info>

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Install `@maxleiter/vcup` globally and how the `vcup` bin maps to `cli.ts`.
</Card>
<Card title="Quickstart" href="/quickstart">
Configure `~/.vcuprc`, upload one file, and verify the proxy URL.
</Card>
<Card title="How it works" href="/how-it-works">
End-to-end flow from CLI stream through Blob to `/f` proxy.
</Card>
<Card title="Deploy on Vercel" href="/deploy-vercel">
Clone deploy button, Blob store, and required server env vars.
</Card>
<Card title="Proxy and raw URLs" href="/proxy-and-raw-urls">
`url` vs `raw`, rewrite behavior, and inline disposition.
</Card>
<Card title="Authentication" href="/authentication">
Shared `VCUP_TOKEN` and which routes are public.
</Card>
</CardGroup>
