# Configuration reference

> ~/.vcuprc keys, client env vars, server env vars, VCUP_BASE_URL override, and validation errors returned by handlers.

- 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

- `cli.ts`
- `.env.example`
- `api/upload.ts`
- `api/f.ts`
- `api/delete.ts`
- `README.md`

---

---
title: "Configuration reference"
description: "~/.vcuprc keys, client env vars, server env vars, VCUP_BASE_URL override, and validation errors returned by handlers."
---

vcup splits configuration across a client-side `~/.vcuprc` (or `VCUP_API_URL` / `VCUP_TOKEN` env vars) that points the CLI at your deployment, and server-side Vercel environment variables that authenticate uploads/deletes, wire Vercel Blob, and shape proxy URLs in upload responses. API handlers in `api/upload.ts`, `api/delete.ts`, and `api/f.ts` return plain-text bodies with fixed status codes for validation failures; the CLI adds its own exit-time errors when config or delete preconditions are missing.

## Configuration surfaces

| Surface | Location | Consumed by |
| --- | --- | --- |
| `~/.vcuprc` | `$HOME/.vcuprc` | CLI (`loadConfig` in `cli.ts`) |
| Client env | Shell / CI | CLI |
| Server env | Vercel project / local `.env` | Serverless handlers + `@vercel/blob` |

```text
  Developer machine                    Vercel deployment
  -----------------                    ------------------
  ~/.vcuprc { url, token }  ------>   POST /api/upload  (VCUP_TOKEN)
  VCUP_API_URL, VCUP_TOKEN            DELETE /api/delete
  BLOB_STORE_URL (rm only)            GET /f/:slug -> api/f.ts

                                      BLOB_READ_WRITE_TOKEN (@vercel/blob)
                                      BLOB_STORE_URL (proxy fetch)
                                      VCUP_BASE_URL (upload JSON url field)
```

## `~/.vcuprc`

The CLI reads a JSON file at `~/.vcuprc` with two string fields. Both are required for a successful upload or delete unless environment variables supply them (see precedence below).

<ParamField body="url" type="string" required>
Deployment origin used as the API base. Requests go to `{url}/api/upload` and `{url}/api/delete`. Use your Vercel app URL or custom domain (no trailing path).
</ParamField>

<ParamField body="token" type="string" required>
Shared secret sent as `Authorization: Bearer {token}`. Must match the server `VCUP_TOKEN` value.
</ParamField>

Example (from CLI help and README):

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

<Warning>
`loadConfig` does not validate JSON shape beyond `JSON.parse`. Missing `url` or `token` keys can produce `undefined` in fetch URLs if only partial env overrides are set.
</Warning>

## Client environment variables

| Variable | Required | Role |
| --- | --- | --- |
| `VCUP_API_URL` | For env-only config | Same as `~/.vcuprc` `url` |
| `VCUP_TOKEN` | For env-only config | Same as `~/.vcuprc` `token` |
| `BLOB_STORE_URL` | For `vcup rm` on proxy URLs only | Public blob store origin used to rewrite `/f/...` to `{store}/{slug}` before delete |

### Precedence (`loadConfig`)

1. If **both** `VCUP_API_URL` and `VCUP_TOKEN` are set, the CLI uses those values and does not read `~/.vcuprc`.
2. If `~/.vcuprc` exists, each field is `process.env.VCUP_* || rc.*` (env overrides per key).
3. Otherwise the CLI prints a sample `~/.vcuprc` JSON snippet and exits with code `1`.

<Note>
`VCUP_API_URL` controls where the CLI calls the API. It does **not** set the hostname embedded in upload response `url` — that comes from server `VCUP_BASE_URL` or the request host (see below).
</Note>

### CLI-only errors (not HTTP handlers)

| Condition | Message / behavior |
| --- | --- |
| No complete config | `Missing config. Set VCUP_API_URL and VCUP_TOKEN env vars, or create ~/.vcuprc:` + sample JSON; exit `1` |
| `vcup rm` with proxy URL, no `BLOB_STORE_URL` | `Cannot resolve proxy URL without BLOB_STORE_URL env var...`; exit `1` |
| Upload/delete HTTP failure | `Upload failed:` / `Delete failed:` + status + response body; exit `1` |

## Server environment variables

`.env.example` documents the deploy-time set; handlers reference a subset directly.

| Variable | Required | Used in code | Purpose |
| --- | --- | --- | --- |
| `BLOB_READ_WRITE_TOKEN` | Yes (Blob SDK) | Not referenced in handlers | Credentials for `@vercel/blob` `put` / `del` on Vercel |
| `BLOB_STORE_URL` | Yes for proxy | `api/f.ts`, CLI delete | Public store URL (e.g. `https://abc123.public.blob.vercel-storage.com`) to build blob fetch URLs |
| `VCUP_TOKEN` | Yes for protected routes | `api/upload.ts`, `api/delete.ts` | Bearer secret; if unset, all authenticated requests get `401` |
| `VCUP_BASE_URL` | No | `api/upload.ts` | Overrides origin in JSON `url` field (`{VCUP_BASE_URL}/f/{slug}`) |

`BLOB_READ_WRITE_TOKEN` is wired by the Vercel Blob integration and the deploy button env list in README; application code assumes it is present when `put` / `del` run.

## `VCUP_BASE_URL` override

On successful upload, the handler builds the proxy link:

```ts
const baseUrl =
  process.env.VCUP_BASE_URL ||
  `${req.headers["x-forwarded-proto"] || "http"}://${req.headers.host}`;
// response: { url: `${baseUrl}/f/${slug}`, raw: blob.url }
```

| Setting | Effect on `url` in upload JSON | Effect on `raw` |
| --- | --- | --- |
| Unset | `https?://{Host}/f/{slug}` from request headers | Vercel Blob URL from `put` |
| Set (e.g. `https://files.example.com`) | `{VCUP_BASE_URL}/f/{slug}` | Unchanged (always `blob.url`) |

<Tip>
For a custom public hostname, set `VCUP_BASE_URL` on the server **and** set client `url` / `VCUP_API_URL` to the same origin so CLI calls and printed links stay consistent.
</Tip>

## Handler validation and error responses

Handlers respond with **plain text** bodies (not JSON) for errors unless noted. Node lowercases incoming header names; the CLI sends `Authorization`, `X-Filename`, and `X-Blob-Url`.

### `POST /api/upload` (`api/upload.ts`)

| Status | Body | When |
| --- | --- | --- |
| `405` | `Method not allowed` | Method is not `POST` |
| `401` | `Unauthorized` | `VCUP_TOKEN` unset on server, or `Authorization: Bearer` value mismatch |
| `400` | `Missing x-filename header` | `x-filename` header absent |
| `200` | JSON `{ "url": string, "raw": string }` | Success |

Protected check (both must pass): server has `process.env.VCUP_TOKEN` **and** bearer token equals it.

### `DELETE /api/delete` (`api/delete.ts`)

| Status | Body | When |
| --- | --- | --- |
| `405` | `Method not allowed` | Method is not `DELETE` |
| `401` | `Unauthorized` | Same Bearer rules as upload |
| `400` | `Missing x-blob-url header` | `x-blob-url` header absent |
| `200` | `Deleted` | Success (plain text) |

The CLI sets `X-Blob-Url` to the raw blob URL (after optional proxy→store rewrite).

### `GET /f/:slug` → `api/f.ts` (public, no Bearer)

| Status | Body | When |
| --- | --- | --- |
| `405` | `Method not allowed` | Method is not `GET` |
| `400` | `Missing file slug` | Path has no slug after `/f/` |
| `500` | `BLOB_STORE_URL not configured` | `BLOB_STORE_URL` unset |
| `{upstream}` | `Not found` | Blob fetch returned non-OK (e.g. `404`) |
| `502` | `Failed to fetch file` | Fetch threw |
| `200` | Streamed body | Success; `Content-Disposition: inline`, long cache |

Slug extraction: pathname with `/^\/f\//` removed via `vercel.json` rewrite `{ "source": "/f/:slug*", "destination": "/api/f" }`.

## Matching client and server settings

<Steps>
<Step title="Set server secrets on Vercel">
Add `BLOB_READ_WRITE_TOKEN`, `BLOB_STORE_URL`, and `VCUP_TOKEN` from Storage / your chosen secret. Optionally set `VCUP_BASE_URL` for a custom public origin.
</Step>
<Step title="Point the CLI at the deployment">
Create `~/.vcuprc` with `url` = deployment origin and `token` = the same string as `VCUP_TOKEN`, or export `VCUP_API_URL` and `VCUP_TOKEN`.
</Step>
<Step title="Optional: proxy deletes from the CLI">
If you delete by proxy URL (`.../f/...`), set client `BLOB_STORE_URL` to the store public URL, or pass the raw blob URL from upload `--raw`.
</Step>
</Steps>

## Quick reference: who reads what

| Name | Client | Server upload | Server delete | Server proxy (`/f`) |
| --- | --- | --- | --- | --- |
| `url` / `VCUP_API_URL` | API base | — | — | — |
| `token` / `VCUP_TOKEN` | Bearer | Auth | Auth | — |
| `VCUP_BASE_URL` | — | Response `url` | — | — |
| `BLOB_STORE_URL` | `rm` rewrite | — | — | Fetch blob |
| `BLOB_READ_WRITE_TOKEN` | — | Blob `put` | Blob `del` | — |

## Related pages

<CardGroup>
<Card title="Configure the CLI" href="/configure-cli">
`~/.vcuprc` schema, env precedence, and aligning client settings with your deployment.
</Card>
<Card title="Environment variables" href="/environment-variables">
Focused index of server and client variable names.
</Card>
<Card title="Authentication" href="/authentication">
Bearer token flow on upload and delete; public proxy route.
</Card>
<Card title="Custom domain" href="/custom-domain">
`VCUP_BASE_URL` plus client `url` for a single public hostname.
</Card>
<Card title="API reference" href="/api-reference">
Full route contracts, headers, and response shapes.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Common misconfigurations mapped to status codes and CLI errors.
</Card>
</CardGroup>
