Coco App `upload_attachment` Requirements and File Storage Reference Guide

The upload_attachment Tauri command requires a valid server ID, absolute file paths with UTF-8 filenames, and returns unique attachment IDs that are later referenced via get_attachment_by_ids to retrieve stored files from the Coco server.

In the infinilabs/coco-app repository, attachment handling is implemented as a core Tauri command that bridges the frontend and a remote Coco server. Understanding the strict validation requirements and the identifier-based storage pattern is essential for building file upload features that integrate properly with the chat and assistant workflows.

Prerequisites for upload_attachment

Before invoking the command, the system validates several constraints defined in src-tauri/src/server/attachment.rs. Failure to meet any requirement results in specific error variants returned to the caller.

Server Configuration Requirements

The command accepts a server_id parameter that must reference an existing server configuration registered in the application. According to the implementation in src-tauri/src/server/attachment.rs (lines 96-101), if the provided ID does not correspond to a known server, the command returns a ServerNotFound error immediately.

Additionally, if the target server configuration includes an API token, the command automatically attaches it as an X-API-TOKEN header during the HTTP request (lines 104-108).

File Path and Naming Constraints

The file_paths parameter must be a Vec<PathBuf> containing absolute paths to existing, readable files. The implementation performs the following validations:

  • File existence: Each path is checked to confirm it points to a valid file on the local filesystem (lines 61-73).
  • Filename extraction: The path must yield a filename that can be extracted for the multipart upload (lines 78-82).
  • UTF-8 encoding: The filename must be valid UTF-8; otherwise, the command returns a NonUtf8Filename error (lines 84-88).

How Attachments Are Stored and Referenced

The storage lifecycle follows a distributed identifier pattern: files are uploaded once, referenced by immutable IDs in chat contexts, and retrieved on-demand via a dedicated search endpoint.

Upload Response and Attachment IDs

Upon successful multipart upload, the Coco server returns an UploadAttachmentResponse struct containing:

  • acknowledged: A boolean confirming receipt
  • attachments: A Vec<String> of unique attachment identifiers

These identifiers are generated server-side and serve as the canonical reference for the files. The struct definition is found in src-tauri/src/server/attachment.rs (lines 17-24).

Linking Attachments to Chat Sessions

The returned IDs are not automatically persisted; the caller must explicitly include them in subsequent chat operations. As defined in src-tauri/src/common/assistant.rs (lines 8-10), requests to chat_create or chat_chat accept an optional attachments field of type Option<Vec<String>>.

The typical flow stores these IDs in the SessionContext.attachments vector before initiating a conversation, ensuring the server associates the uploaded files with the specific chat session.

Retrieving Files with get_attachment_by_ids

To access stored files later, the system uses the get_attachment_by_ids command implemented in src-tauri/src/server/attachment.rs (lines 27-49). This function:

  1. Accepts a server_id and a Vec<String> of attachment IDs
  2. Constructs a JSON body: {"attachments": ["id1", "id2"]}
  3. POSTs to the /attachment/_search endpoint
  4. Returns the raw JSON payload containing URLs or binary metadata

This retrieval pattern decouples the upload operation from file consumption, allowing attachments to be referenced across multiple sessions without re-uploading.

Implementation Examples

Uploading Files from the Frontend

Invoke the command from JavaScript using the Tauri API:

async function uploadFiles(serverId, filePaths) {
  try {
    const response = await window.__TAURI__.invoke('upload_attachment', {
      server_id: serverId,
      file_paths: filePaths  // Array of absolute paths
    });
    
    // response: { acknowledged: true, attachments: ['abc123', 'def456'] }
    console.log('Attachment IDs:', response.attachments);
    return response.attachments;
  } catch (e) {
    console.error('Upload failed:', e);
    throw e;
  }
}

// Example usage
uploadFiles('production-server', ['/Users/me/report.pdf', '/Users/me/data.csv']);

Attaching Files to Chat Requests

When constructing a chat request in Rust, include the IDs returned from upload_attachment:

use serde_json::json;

// `ids` is the Vec<String> returned from upload_attachment
let chat_payload = json!({
    "message": "Please analyze these documents",
    "attachments": ids  // Links files to this message
});

This structure is processed by the chat orchestration layer in src-tauri/src/assistant/mod.rs.

Fetching Stored Attachments

Retrieve metadata or URLs for previously uploaded files:

// Within a Tauri command or async context
let attachment_ids = vec!["abc123".to_string(), "def456".to_string()];
let payload = get_attachment_by_ids(server_id, attachment_ids).await?;
println!("Retrieved: {}", payload);

From the frontend:

const results = await window.__TAURI__.invoke('get_attachment_by_ids', {
  server_id: 'production-server',
  attachments: ['abc123', 'def456']
});

Summary

  • upload_attachment requires a valid server_id, absolute file paths, and UTF-8 filenames, returning NonUtf8Filename or ServerNotFound errors when constraints are violated.
  • Authentication is handled automatically via the X-API-TOKEN header when the server configuration includes an API token.
  • Storage references are immutable string IDs returned in the UploadAttachmentResponse, not the files themselves.
  • Chat integration occurs by passing these IDs to the attachments parameter in chat creation or messaging requests.
  • Retrieval is performed via get_attachment_by_ids, which queries the /attachment/_search endpoint with a JSON body containing the ID list.

Frequently Asked Questions

What happens if a filename contains non-UTF-8 characters?

The upload_attachment command in src-tauri/src/server/attachment.rs (lines 84-88) explicitly validates UTF-8 encoding. If the filename contains invalid UTF-8 sequences, the command returns a NonUtf8Filename error before attempting the upload. The caller must sanitize or rename the file before retrying.

How are authentication tokens handled during upload?

If the server configuration associated with the provided server_id contains an api_token field, the command automatically injects it into the request headers as X-API-TOKEN. This occurs during the HTTP client construction phase in src-tauri/src/server/attachment.rs (lines 104-108), requiring no manual header management from the frontend.

Can I upload multiple files in a single request?

Yes. The file_paths parameter accepts a Vec<PathBuf>, allowing batch uploads. The command iterates over all paths, validates each file individually, and constructs a multipart request containing all valid files. The server returns a corresponding list of attachment IDs in the attachments field of the response.

Where are attachment IDs stored between upload and retrieval?

The Coco app does not persist attachment IDs automatically. The caller is responsible for storing the returned Vec<String> in application state, typically within a SessionContext struct, or passing them immediately to chat_create or chat_chat requests. The IDs are ephemeral references that must be maintained by the client until needed for retrieval via get_attachment_by_ids.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →