# Grok2API Media Storage Support: Local Filesystem Implementation Explained

> Grok2API supports local filesystem media storage, including JPEG, PNG, WebP, and GIF. Learn about its implementation with atomic writes and path traversal protection.

- Repository: [Chenyme/grok2api](https://github.com/chenyme/grok2api)
- Tags: deep-dive
- Published: 2026-07-16

---

**Grok2API stores generated media exclusively on the local filesystem using the `LocalStore` type, supporting JPEG, PNG, WebP, and GIF formats with atomic writes and path traversal protection.**

The chenyme/grok2api repository implements a self-contained media storage system designed for generated images. Unlike distributed systems that rely on object storage services, **Grok2API media storage** is intentionally limited to the local filesystem, providing a simple, atomic, and secure storage mechanism without external dependencies.

## Local Filesystem Architecture

The storage implementation centers on the `LocalStore` type defined in [`backend/internal/infra/media/local_store.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/infra/media/local_store.go). This design prioritizes data integrity and security through several deliberate architectural constraints.

### Root-Based Security Model

All media files are confined within a designated root directory supplied during store initialization. The `NewLocalStore(root)` constructor enforces this boundary, preventing path traversal attacks by validating that all file operations remain within the specified directory hierarchy. This approach ensures that the application cannot accidentally—or maliciously—write files outside the intended storage location.

### Atomic Commit Mechanism

To prevent data corruption during concurrent writes, the implementation utilizes an atomic commit pattern. When saving images, the system first writes data to a temporary file and then uses `os.Link` to atomically link the file to its final destination. This guarantees *no-replace* semantics for identical IDs, ensuring that existing files are never partially overwritten and that concurrent save operations for the same identifier result in deterministic behavior.

### Supported Image Formats

The store validates MIME types against a strict whitelist before persisting data. The `imageExtension` function maps accepted types to file extensions:

- **JPEG** → `.jpg`
- **PNG** → `.png`
- **WebP** → `.webp`
- **GIF** → `.gif`

Any request with a non-supported MIME type is rejected before filesystem operations begin, preventing the storage of arbitrary or potentially malicious file types.

## Storage API Implementation

The `LocalStore` struct implements the `media.Store` interface, serving as the sole concrete storage backend in the current codebase. The HTTP handler in [`backend/internal/transport/http/media/handler.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/transport/http/media/handler.go) injects this implementation, receiving image data from clients and delegating persistence operations to the store.

To initialize a local store, applications supply a root directory path:

```go
import (
    "context"
    "io/ioutil"
    "log"

    localmedia "github.com/chenyme/grok2api/main/backend/internal/infra/media"
)

func exampleSave() {
    // Create a store rooted at "./media"
    store, err := localmedia.NewLocalStore("./media")
    if err != nil {
        log.Fatalf("cannot create store: %v", err)
    }

    // Load image data (e.g., from an HTTP request)
    data, _ := ioutil.ReadFile("sample.png")

    // Store the image – `id` must be a unique identifier (e.g., UUID)
    key, err := store.SaveImage(context.Background(), "a1b2c3d4e5", "image/png", data)
    if err != nil {
        log.Fatalf("save failed: %v", err)
    }

    // `key` is the storage key that can be later used to retrieve the file
    log.Printf("image stored as %s", key)
}

```

## Performing CRUD Operations

The `LocalStore` provides a complete lifecycle API for media assets, with each method checking request contexts for cancellation and enforcing path validation rules.

### Saving Images with SaveImage

The `SaveImage` method handles the atomic persistence workflow. It accepts a context, unique identifier, MIME type, and byte slice, returning a storage key that can be used for subsequent retrieval. The method validates the MIME type against the supported extensions map before attempting filesystem operations.

### Retrieving Media with Open

To access stored content, the `Open` method returns an `io.ReadCloser` for streaming or reading the file data:

```go
func exampleOpen(key string) {
    store, _ := localmedia.NewLocalStore("./media")
    rc, err := store.Open(context.Background(), key)
    if err != nil {
        log.Fatalf("open failed: %v", err)
    }
    defer rc.Close()
    // `rc` implements io.ReadCloser – read or stream the image as needed
}

```

### Deleting Assets with Delete

The `Delete` method removes the stored file associated with the provided key, handling context cancellation checks and returning errors if the file does not exist or cannot be removed:

```go
func exampleDelete(key string) {
    store, _ := localmedia.NewLocalStore("./media")
    if err := store.Delete(context.Background(), key); err != nil {
        log.Fatalf("delete failed: %v", err)
    }
}

```

## HTTP Integration and Domain Models

The transport layer in [`backend/internal/transport/http/media/handler.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/transport/http/media/handler.go) bridges incoming HTTP requests to the storage backend. Domain models in [`backend/internal/domain/media/asset.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/domain/media/asset.go) represent stored media assets through the storage key abstraction, while [`backend/internal/domain/media/job.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/domain/media/job.go) defines background jobs that may generate media and subsequently utilize the store.

Unit tests in [`backend/internal/infra/media/local_store_test.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/infra/media/local_store_test.go) cover the complete save, open, and delete operation lifecycle, ensuring reliability across concurrent access patterns.

## Summary

- **Grok2API supports only local filesystem storage** via the `LocalStore` implementation in [`backend/internal/infra/media/local_store.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/infra/media/local_store.go).
- **Security is enforced through root-based directory confinement**, preventing path traversal attacks.
- **Atomic writes prevent data corruption** using temporary files and `os.Link` operations.
- **Only four image formats are accepted**: JPEG, PNG, WebP, and GIF.
- **No cloud backends** (S3, GCS, Azure Blob) are currently implemented; extending support would require implementing the `media.Store` interface.

## Frequently Asked Questions

### Does Grok2API support cloud storage like Amazon S3 or Google Cloud Storage?

No. The current codebase only implements the `LocalStore` type for filesystem storage. While the `media.Store` interface theoretically supports alternative backends, no concrete implementations for S3, GCS, or Azure Blob exist in the repository. To use cloud storage, developers must implement the `media.Store` interface with their chosen provider.

### What image formats can Grok2API store?

Grok2API accepts JPEG, PNG, WebP, and GIF formats exclusively. The `imageExtension` function in [`backend/internal/infra/media/local_store.go`](https://github.com/chenyme/grok2api/blob/main/backend/internal/infra/media/local_store.go) maps these MIME types to their respective file extensions (`.jpg`, `.png`, `.webp`, `.gif`). Requests with unsupported MIME types are rejected before filesystem writes occur.

### How does Grok2API prevent file path traversal attacks?

The storage system validates all paths against a root directory supplied during `NewLocalStore` initialization. This design ensures that all file operations—writes, reads, and deletes—are confined to the specified directory tree, preventing the application from accessing or modifying files outside the intended storage location regardless of input validation.

### Is media storage persistent across container restarts?

Yes, because `LocalStore` persists data directly to the host filesystem. However, in containerized environments, persistence requires mounting a volume to the root directory path provided to `NewLocalStore`. Without proper volume mounts, container restarts will result in data loss since the storage is local to the container's filesystem by default.