# add_coco_server Data Payload Structure and Validation in the Coco App

> Learn the add_coco_server data payload structure and validation process in the Coco App. Discover how Tauri constructs, validates, and persists server entries for robust security.

- Repository: [INFINI Labs/coco-app](https://github.com/infinilabs/coco-app)
- Tags: api-reference
- Published: 2026-03-04

---

**The `add_coco_server` Tauri command accepts only a base URL string from the frontend, then constructs a complete `Server` data payload by querying the remote `provider/_info` endpoint and validates it through duplicate detection, HTTP health checks, and strict Serde deserialization before persisting the entry.**

The `add_coco_server` function in the **infinilabs/coco-app** repository serves as the primary backend interface for registering new Coco search servers in the desktop application. Rather than receiving a complete data object from the frontend, the command orchestrates a sophisticated validation pipeline that fetches, transforms, and verifies server metadata before storage.

## Input Parameters: Minimal Frontend Contract

The frontend invocation supplies **only one argument**: the server’s base URL (`endpoint`). The command signature in [`src-tauri/src/server/servers.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/server/servers.rs) accepts this single string and immediately begins the payload construction process.

```typescript
// Frontend invocation (TypeScript)
import { invoke } from '@tauri-apps/api/tauri';

const server = await invoke<Server>('add_coco_server', { 
  endpoint: 'https://my.coco.server' 
});

```

## Data Payload Construction Pipeline

The backend constructs the full data payload through a sequence of network requests and transformations. This occurs in the `add_coco_server` implementation at lines 1000–1069 of [`src-tauri/src/server/servers.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/server/servers.rs).

### Endpoint Sanitization and Duplicate Detection

First, the command trims trailing slashes from the supplied URL and checks for existing entries via `check_endpoint_exists`. If the endpoint already exists in the local store, the command aborts immediately with an error response.

```rust
// servers.rs:1000-1008
let endpoint = trim_endpoint_last_forward_slash(&endpoint);
if check_endpoint_exists(&endpoint) {
    return Err("Server already exists".to_string());
}

```

### Fetching Provider Metadata

The command constructs the URL `<endpoint>/provider/_info` using `provider_info_url(endpoint)` and sends a GET request via `HttpClient::send_raw_request`. This fetches the server’s public metadata including provider details, version information, and authentication configuration.

### HTTP Response Validation

If the remote server returns any status other than **200 OK**, the command returns the error message *"This Coco server is possibly down"* and terminates. The response body is then read as text using `get_response_body_text`, with I/O errors converted to user-friendly messages via `report_error`.

### JSON Deserialization into Server Struct

The response body is parsed using **Serde** (`serde_json::from_str`) into the `Server` struct. Deserialization failures propagate as *"Failed to deserialize the response"*, enforcing strict schema compliance.

```rust
// servers.rs:1030-1032
let mut server: Server = serde_json::from_str(&body)
    .map_err(|e| format!("Failed to deserialize the response: {}", e))?;

```

## The Server Struct: Complete Payload Definition

The final data payload structure is defined in [`src-tauri/src/common/server.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/common/server.rs) at lines 40–74. This struct represents the complete server configuration persisted to local storage.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `String` | Unique identifier (auto-generated UUID if absent) |
| `builtin` | `bool` | Flag for internal system servers |
| `name` | `String` | Human-readable display name (defaults to *Coco Server*) |
| `endpoint` | `String` | Base URL of the remote server |
| `provider` | `Provider` | Metadata including name, icon, website, EULA, privacy policy |
| `version` | `Version` | Server version string |
| `minimal_client_version` | `Option<MinimalClientVersion>` | Minimum compatible client version |
| `updated` | `String` | ISO timestamp of last provider-info fetch |
| `enabled` | `bool` | UI visibility flag (default `true`) |
| `public` | `bool` | Indicates if server requires authentication |
| `available` | `bool` | Calculated availability status |
| `health` | `Option<Health>` | Runtime health-check data |
| `profile` | `Option<UserProfile>` | Authenticated user profile |
| `auth_provider` | `AuthProvider` | SSO configuration URL |
| `priority` | `u32` | Display ordering hint |
| `stats` | `Option<HashMap<String, Value>>` | Runtime statistics |

## Validation Logic Implementation

The command performs four distinct validation layers to ensure data integrity before persistence.

### Duplicate Endpoint Prevention

The `check_endpoint_exists` function queries the local server registry to prevent duplicate registrations. This occurs before any network requests, ensuring idempotent behavior.

### Network Health Verification

The command strictly validates that the `provider/_info` endpoint returns HTTP 200. Any other status code—including redirects or authentication challenges—results in immediate failure with a descriptive error message.

### Schema Validation via Serde

The deserialization step enforces type safety through Rust’s type system. Required fields must be present and correctly typed. Optional fields utilize `#[serde(default = ...)]` attributes to populate sensible defaults when missing from the remote response.

### Logical Consistency and Normalization

Post-deserialization, the command applies business logic validation:

1. **ID Generation**: If the remote response lacks an `id`, the command generates a new UUID via `Uuid::new`
2. **Name Fallback**: Empty `name` fields default to *"Coco Server"*
3. **Availability Calculation**: 
   - Public servers (`public: true`) automatically set `available: true`
   - Private servers check the token cache via `get_server_token(&server.id)` and assert that no token exists for new servers, forcing `available: false` until authentication occurs

```rust
// servers.rs:1046-1058
if server.public {
    server.available = true;
} else {
    assert!(get_server_token(&server.id).is_none(), "New private server should not have existing token");
    server.available = false;
}

```

## Code Implementation Examples

### Frontend TypeScript Integration

```typescript
import { invoke } from '@tauri-apps/api/tauri';
import type { Server } from '@/types';

async function registerCocoServer(endpoint: string): Promise<Server> {
  try {
    const server = await invoke<Server>('add_coco_server', { endpoint });
    console.log('Server registered:', server.id, server.name);
    return server;
  } catch (err) {
    console.error('Registration failed:', err);
    throw err;
  }
}

```

### Backend Rust Command Signature

```rust
// src-tauri/src/lib.rs
#[tauri::command]
pub async fn add_coco_server(
    app_handle: AppHandle, 
    endpoint: String
) -> Result<Server, String> {
    // Implementation in src-tauri/src/server/servers.rs
}

```

## Summary

- **Single Input**: The command requires only a base URL endpoint from the frontend, constructing the full payload server-side
- **Network Dependency**: Validation requires successful HTTP 200 response from the remote `provider/_info` endpoint
- **Strict Schema**: The `Server` struct in [`common/server.rs`](https://github.com/infinilabs/coco-app/blob/main/common/server.rs) defines the complete payload shape with Serde enforcing type safety
- **Auto-Generation**: Missing `id` fields receive UUIDs and empty names default to *"Coco Server"* during normalization
- **Availability Logic**: Public servers are immediately available; private servers require authentication before becoming available
- **Persistence**: Valid servers are saved via `save_server` and `persist_servers` after passing all validation checks

## Frequently Asked Questions

### What data does the frontend need to send to add_coco_server?

The frontend sends only a single string parameter: `endpoint`. This is the base URL of the Coco server (e.g., `https://search.example.com`). The backend handles all metadata retrieval and payload construction by querying the server’s `provider/_info` endpoint.

### How does add_coco_server prevent duplicate server entries?

Before making network requests, the command calls `check_endpoint_exists` to verify the trimmed URL is not already registered. If a match is found, the function returns an error immediately without contacting the remote server.

### What determines if a server is marked as available?

The `available` boolean is set based on the server’s `public` flag and token cache state. Public servers are automatically marked available (`true`). Private servers must not have an existing token in the cache and are forced to `available: false` until the user authenticates.

### Where is the Server data structure defined?

The `Server` struct is defined in [`src-tauri/src/common/server.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/common/server.rs) at lines 40–74. This file contains the complete payload schema including nested types like `Provider`, `Version`, `Health`, and `AuthProvider` that populate the final server object returned to the frontend.