# File Attachments Guide

TokST enables you to attach files to memories, providing rich context alongside text. Files are stored in Cloudflare R2 with metadata indexed in Supabase.

## Overview

When you attach a file to a memory, TokST:

1. Uploads the file to Cloudflare R2 object storage
2. Stores metadata (filename, size, MIME type, URL) in Supabase
3. Associates the attachment with the memory
4. Generates a **presigned upload URL** (valid for 1 hour) and **presigned download URLs** (valid for 15 minutes)

This means files are never stored in the database directly — they live in R2, keeping your database lean and fast.

## CLI Usage

### Attach During Creation

Use the `--file` flag when storing a new memory. The flag is repeatable for multiple files.

```bash
# Single file
tokst remember "Sprint retrospective notes" --file retro-notes.md

# Multiple files
tokst remember "Q3 planning documents" --file roadmap.pdf --file budget.xlsx --file timeline.png
```

The CLI accepts local files. For a remote URL, use the remote MCP `fileUrl` input or download the file locally before passing `--file`.

### Attach to an Existing Memory

Use the `attach` subcommand:

```bash
tokst memory attach <memory-id> --file document.pdf
```

### Download Attachments

Pull a memory's files back to your local machine with the `download` subcommand. Files are streamed from R2 via a presigned GET URL and saved with their original filenames.

```bash
# Download all attachments to ~/Downloads
tokst memory download <memory-id>

# Specify an output directory (auto-created if missing, ~ expands to $HOME)
tokst memory download <memory-id> --out ./files

# Download a single attachment by ID
tokst memory download <memory-id> --attachment-id <attachment-id>

# Machine-readable output
tokst memory download <memory-id> --json
```

> Download is cloud-only. Local SQLite mode throws — run `tokst login --key <api-key>` first.

## MCP Usage

When using the MCP server, pass file parameters in the tool call:

```json
{
  "content": "Meeting notes with attached diagram",
  "filePath": "/home/user/diagram.png"
}
```

Or with a remote URL:

```json
{
  "content": "Reference architecture",
  "fileUrl": "https://example.com/architecture.png"
}
```

To attach a file to an existing memory via MCP, use `tokst_attach_file`:

```json
{
  "memoryId": "mem_abc123",
  "fileUrl": "https://example.com/report.pdf",
  "filename": "report.pdf"
}
```

ChatGPT uploads are bound automatically through the tool's `openai/fileParams` metadata. To download the file again, call `tokst_download_file`:

```json
{
  "memoryId": "mem_abc123",
  "attachmentId": "attachment-uuid"
}
```

The remote MCP server returns a 15-minute signed URL as both JSON and MCP `resource_link` content. Remote MCP uploads have a 50 MB per-file limit.

## Upload Confirmation

An attachment stays `pending` until confirmation. The confirmation route performs an R2 `HEAD` request, rejects missing or empty objects with `409`, and records the actual object size and MIME type before changing the attachment to `active`. Pending or failed uploads older than two hours are cleaned up automatically.

## Web Dashboard

The web dashboard provides a drag-and-drop interface for file uploads with real-time progress indicators. Navigate to any memory and use the attachment panel to upload files.

## Storage Quotas

Plans have the following storage limits for file attachments:

| Plan | Storage Quota |
|---|---|
| **Free** | 500 MB |
| **Starter** | 2 GB |
| **Pro** | 10 GB |
| **Team workspace** | 20 GB shared |
| **Legacy Max / Team** | Existing entitlement |

| Upload Channel | Per-file Behavior |
|---|---|
| Web dashboard | 500 MB client-side maximum |
| Remote MCP | 50 MB maximum |
| CLI | Limited by the account's remaining storage quota |

Storage usage is tracked in real time and visible through `tokst status`.

## R2 Path Structure

Files are stored in Cloudflare R2 using the following path convention:

```
{workspace_id}/{atlas_id}/{memory_id}/{attachment_id}-{filename}
```

For example:

```
ws_abc123/atlas_def456/mem_789abc/att_xyz789-report.pdf
```

This structure ensures:

- **Isolation** — Files from different workspaces never collide
- **Discoverability** — You can reconstruct paths from database metadata
- **Cleanup** — Deleting a memory or atlas removes the matching R2 objects before database cleanup

## Security

File access is secured with **presigned URLs**:

| Action | URL Validity |
|---|---|
| Upload | 1 hour |
| Download | 15 minutes |

Presigned URLs are generated server-side and require valid authentication. Direct R2 bucket access is blocked. This ensures that only authenticated users with appropriate permissions can upload or download files.

Object deletion accepts only an exact internal service credential or a user token with matching attachment ownership. User-provided file keys outside the authenticated scope are rejected.

## Best Practices

- **Use meaningful filenames** — The filename becomes part of the R2 path and is displayed in the dashboard
- **Keep files under 100 MB** for faster uploads
- **Use remote MCP `fileUrl`** when an agent needs to attach a public remote asset
- **Monitor your quota** with `tokst status` to avoid hitting storage limits
