---
title: IStorageService Contract
description: Reference for the Storage Service contract — file upload, download, deletion, metadata, and signed URL generation
---

# IStorageService Contract

The Storage Service provides a unified interface for **file management** — uploading, downloading, and organizing files across different storage backends (local filesystem, S3, GCS, Azure Blob).

<Callout type="info">
**Source:** `packages/spec/src/system/contracts/storage-service.ts`  
**Token:** `STORAGE_SERVICE`
</Callout>

---

## Interface Definition

```typescript
export interface IStorageService {
  // Core Operations
  upload(file: StorageUploadInput): Promise<StorageFile>;
  download(path: string): Promise<StorageDownloadResult>;
  delete(path: string): Promise<void>;
  exists(path: string): Promise<boolean>;
  getInfo(path: string): Promise<StorageFile | null>;

  // Listing
  list(prefix?: string, options?: ListOptions): Promise<StorageFile[]>;

  // URLs
  getSignedUrl(path: string, options?: SignedUrlOptions): Promise<string>;
  getPublicUrl(path: string): string;

  // Bulk Operations
  deleteMany(paths: string[]): Promise<BulkDeleteResult>;
  copy(source: string, destination: string): Promise<StorageFile>;
  move(source: string, destination: string): Promise<StorageFile>;
}
```

---

## Core Operations

### upload

Uploads a file to storage. Returns metadata about the stored file.

```typescript
const file = await storageService.upload({
  path: 'attachments/tasks/tsk_01HQ4A7B/document.pdf',
  content: fileBuffer,
  contentType: 'application/pdf',
  metadata: {
    objectType: 'task',
    recordId: 'tsk_01HQ4A7B9D3F5G8J2K4L',
    uploadedBy: 'usr_01HQ3V5K8N2M4P6R7T9W',
  },
});

console.log(file.path);     // "attachments/tasks/tsk_01HQ4A7B/document.pdf"
console.log(file.size);     // 245760
console.log(file.url);      // "https://storage.example.com/..."
```

### download

Downloads a file and returns its content with metadata.

```typescript
const result = await storageService.download(
  'attachments/tasks/tsk_01HQ4A7B/document.pdf'
);

console.log(result.content);     // Buffer
console.log(result.contentType); // "application/pdf"
console.log(result.size);        // 245760
```

### delete

Permanently removes a file from storage.

```typescript
await storageService.delete('attachments/tasks/tsk_01HQ4A7B/document.pdf');
```

### exists

Checks if a file exists at the given path without downloading it.

```typescript
const fileExists = await storageService.exists(
  'attachments/tasks/tsk_01HQ4A7B/document.pdf'
);
// true or false
```

### getInfo

Retrieves file metadata without downloading the content.

```typescript
const info = await storageService.getInfo(
  'attachments/tasks/tsk_01HQ4A7B/document.pdf'
);

if (info) {
  console.log(info.size);        // 245760
  console.log(info.contentType); // "application/pdf"
  console.log(info.createdAt);   // "2025-01-20T10:30:00.000Z"
  console.log(info.metadata);    // { objectType: 'task', ... }
}
```

---

## StorageUploadInput

```typescript
export interface StorageUploadInput {
  path: string;
  content: Buffer | ReadableStream | Uint8Array;
  contentType: string;
  metadata?: Record<string, string>;
  options?: StorageUploadOptions;
}
```

## StorageUploadOptions

Configuration options for upload behavior.

```typescript
export interface StorageUploadOptions {
  // Access control
  visibility?: 'public' | 'private';

  // Overwrite behavior
  overwrite?: boolean;

  // Size limits
  maxSize?: number;

  // Allowed content types
  allowedTypes?: string[];

  // Cache control
  cacheControl?: string;

  // Server-side encryption
  encryption?: 'aes256' | 'kms';
}
```

| Option | Type | Default | Description |
|:---|:---|:---|:---|
| `visibility` | `string` | `'private'` | File access level |
| `overwrite` | `boolean` | `false` | Replace existing file at same path |
| `maxSize` | `number` | `10485760` | Maximum file size in bytes (default: 10MB) |
| `allowedTypes` | `string[]` | `[]` | Restrict content types (empty = allow all) |
| `cacheControl` | `string` | — | HTTP Cache-Control header for served files |
| `encryption` | `string` | — | Server-side encryption algorithm |

<Callout type="tip">
**File Path Convention:** Use the pattern `{category}/{object}/{record_id}/{filename}` for organized storage. Example: `attachments/task/tsk_01HQ4A7B/report.pdf`.
</Callout>

---

## Listing Files

### list

Lists files with an optional prefix filter and pagination.

```typescript
// List all attachments for a task
const files = await storageService.list(
  'attachments/tasks/tsk_01HQ4A7B/',
  { limit: 50, cursor: undefined }
);

for (const file of files) {
  console.log(`${file.path} (${file.size} bytes)`);
}
```

---

## URL Generation

### getSignedUrl

Generates a temporary signed URL for private file access.

```typescript
const url = await storageService.getSignedUrl(
  'attachments/tasks/tsk_01HQ4A7B/document.pdf',
  {
    expiresIn: 3600,     // 1 hour
    method: 'GET',
  }
);
// "https://storage.example.com/...?signature=abc&expires=..."
```

### getPublicUrl

Returns the permanent public URL for files with `visibility: 'public'`.

```typescript
const url = storageService.getPublicUrl('assets/logo.png');
// "https://storage.example.com/assets/logo.png"
```

---

## SignedUrlOptions

```typescript
export interface SignedUrlOptions {
  expiresIn: number;              // Seconds until URL expires
  method?: 'GET' | 'PUT';        // HTTP method the URL is valid for
  contentType?: string;           // Required content type for PUT URLs
  responseDisposition?: string;   // Content-Disposition header override
}
```

---

## Bulk Operations

### deleteMany

Deletes multiple files in a single operation.

```typescript
const result = await storageService.deleteMany([
  'attachments/tasks/tsk_01HQ4A7B/doc1.pdf',
  'attachments/tasks/tsk_01HQ4A7B/doc2.pdf',
  'attachments/tasks/tsk_01HQ4A7B/image.png',
]);

console.log(result.deleted);  // 3
console.log(result.errors);   // []
```

### copy / move

```typescript
// Copy a file to a new location
const copied = await storageService.copy(
  'templates/report.pdf',
  'attachments/tasks/tsk_01HQ4A7B/report.pdf'
);

// Move a file (copy + delete original)
const moved = await storageService.move(
  'uploads/temp/file.pdf',
  'attachments/tasks/tsk_01HQ4A7B/file.pdf'
);
```

---

## StorageFile

The return type for file metadata.

```typescript
export interface StorageFile {
  path: string;
  url: string;
  size: number;
  contentType: string;
  visibility: 'public' | 'private';
  createdAt: string;
  updatedAt: string;
  metadata?: Record<string, string>;
  etag?: string;
}
```

---

## Error Codes

| Code | Description |
|:---|:---|
| `FILE_NOT_FOUND` | No file exists at the specified path |
| `FILE_TOO_LARGE` | File exceeds the configured `maxSize` |
| `INVALID_TYPE` | Content type is not in `allowedTypes` |
| `FILE_EXISTS` | File already exists and `overwrite` is false |
| `STORAGE_FULL` | Storage quota has been exceeded |
| `PERMISSION_DENIED` | User lacks access to the file or bucket |
