# How enable_server and disable_server Commands Manage Server Connectivity in the Coco App

> Learn how enable_server and disable_server commands in Infinilabs' Coco app manage server connectivity across a five-layer architecture updating UI, persisting configs, and synchronizing search.

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

---

**The enable_server and disable_server commands in the Coco app orchestrate a five-layer architecture that updates the UI state, persists configuration changes, and synchronizes the search source registry by routing calls from React hooks through TypeScript adapters to Rust backend commands.**

The **Coco** app from `infinilabs/coco-app` manages server connectivity through a coordinated stack spanning the frontend UI, platform abstraction layer, and Rust backend. These **enable_server and disable_server commands** ensure that enabling or disabling a server updates the local store, persists the configuration to disk, and registers or deregisters the server with the search subsystem. The implementation guarantees consistency across the React state, Tauri runtime, and persistent storage.

## Command Flow Architecture

The implementation spans five distinct layers, each responsible for a specific aspect of the command execution pipeline.

### UI Layer: The useServers Hook

The entry point for toggling server connectivity resides in [`src/hooks/useServers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useServers.ts). This React hook provides the `enableServer` and `disableServer` callbacks that components use to initiate state changes.

The hook retrieves the current window's service, validates its ID, and routes the request to the platform adapter:

```typescript
const enableServer = useCallback(async (enabled: boolean) => {
  const service = await getCurrentWindowService();
  if (!service?.id) throw new Error("No current service selected");
  
  if (enabled) {
    await platformAdapter.commands("enable_server", service.id);
  } else {
    await platformAdapter.commands("disable_server", service.id);
  }
  
  await setCurrentWindowService({ ...service, enabled });
  await getAllServerList();
}, []);

```

After invoking the platform command, the hook updates the local store via `setCurrentWindowService` and refreshes the full server list to synchronize the UI.

### Platform Abstraction: platformAdapter

The [`src/utils/platformAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/platformAdapter.ts) file exposes a unified API that works across different runtime environments. Currently, the app uses the Tauri adapter, which forwards all commands to the native backend.

```typescript
import { createTauriAdapter } from "./tauriAdapter";
let platformAdapter = createTauriAdapter();
export default platformAdapter;

```

This abstraction allows the UI to remain agnostic about whether it is running inside the Tauri shell or a web environment.

### Command Resolution: tauriWrappers

The [`src/utils/wrappers/tauriWrappers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/wrappers/tauriWrappers.ts) file contains the `commandWrapper` object that maps string command names to their concrete TypeScript implementations. This resolution layer bridges generic command invocations to specific functions.

```typescript
export const commandWrapper = {
  async commands<T>(commandName: string, ...args: any[]): Promise<T> {
    if (commandName in commands) {
      return (commands as any)[commandName](...args);
    }
    throw new Error(`Command ${commandName} not found`);
  },
};

```

When the UI calls `"enable_server"`, this wrapper resolves the string to the actual `enable_server` function exported from [`src/commands/servers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/commands/servers.ts).

### TypeScript Bridge: servers.ts

Located at [`src/commands/servers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/commands/servers.ts), this layer provides thin wrappers that forward requests to the Tauri runtime via `invokeWithErrorHandler`. This utility handles authentication checks, error logging, and communication with the `@tauri-apps/api/core` module.

```typescript
export function enable_server(id: string): Promise<void> {
  return invokeWithErrorHandler(`enable_server`, { id });
}

export function disable_server(id: string): Promise<void> {
  return invokeWithErrorHandler(`disable_server`, { id });
}

```

The `invokeWithErrorHandler` function (lines 37-84 in the same file) builds the final bridge to the Rust backend, ensuring consistent error handling across all server commands.

### Rust Implementation: servers.rs

The actual state mutation occurs in [`src-tauri/src/server/servers.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/server/servers.rs). Both commands are defined as async Tauri command handlers that update the server's `enabled` flag, persist the change, and manage the search source registry.

**Enable Server Implementation:**

```rust
#[tauri::command]
pub async fn enable_server(app_handle: AppHandle, id: String) -> Result<(), ()> {
    let Some(mut server) = get_server_by_id(id.as_str()).await else {
        panic!("Server {} does not exist!", id);
    };
    server.enabled = true;
    save_server(&server).await;
    try_register_server_to_search_source(app_handle.clone(), &server).await;
    persist_servers(&app_handle).await.expect("failed to save servers");
    Ok(())
}

```

**Disable Server Implementation:**

```rust
#[tauri::command]
pub async fn disable_server(app_handle: AppHandle, id: String) -> Result<(), ()> {
    let Some(mut server) = get_server_by_id(id.as_str()).await else {
        panic!("Server {} does not exist!", id);
    };
    server.enabled = false;
    let registry = app_handle.state::<SearchSourceRegistry>();
    registry.remove_source(id.as_str()).await;
    save_server(&server).await;
    persist_servers(&app_handle).await.expect("failed to save servers");
    Ok(())
}

```

The **enable_server** command sets `server.enabled = true`, persists the server via `save_server`, registers it with the search subsystem via `try_register_server_to_search_source`, and then calls `persist_servers` to write the full server list to disk. Conversely, **disable_server** sets the flag to `false`, removes the source from the `SearchSourceRegistry`, and persists the updated configuration.

## Practical Implementation Examples

### Using the React Hook in a Component

Components interact with the command system through the `useServers` hook rather than calling commands directly:

```tsx
import { useServers } from "@/hooks/useServers";

function ServerToggle({ serverId }: { serverId: string }) {
  const { enableServer } = useServers();

  const toggle = async (e: React.ChangeEvent<HTMLInputElement>) => {
    await enableServer(e.target.checked);
  };

  return <input type="checkbox" checked={server.enabled} onChange={toggle} />;
}

```

### Direct Platform Adapter Invocation

For utilities or non-React contexts, you can invoke commands directly through the platform adapter:

```typescript
import platformAdapter from "@/utils/platformAdapter";

// Enable server with id "abc123"
await platformAdapter.commands("enable_server", "abc123");

// Disable the same server
await platformAdapter.commands("disable_server", "abc123");

```

### Accessing the Command Wrapper Directly

When you need to bypass the platform abstraction for testing or specialized use cases:

```typescript
import { commandWrapper } from "@/utils/wrappers/tauriWrappers";

await commandWrapper.commands<void>("enable_server", "abc123");

```

## Summary

- **The enable_server and disable_server commands** traverse five architectural layers: React hooks ([`useServers.ts`](https://github.com/infinilabs/coco-app/blob/main/useServers.ts)), platform adapter ([`platformAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/platformAdapter.ts)), command wrapper ([`tauriWrappers.ts`](https://github.com/infinilabs/coco-app/blob/main/tauriWrappers.ts)), TypeScript bridge ([`servers.ts`](https://github.com/infinilabs/coco-app/blob/main/servers.ts)), and Rust backend ([`servers.rs`](https://github.com/infinilabs/coco-app/blob/main/servers.rs)).
- The Rust implementation in [`src-tauri/src/server/servers.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/server/servers.rs) performs the actual state mutation by setting the `enabled` boolean, persisting via `save_server` and `persist_servers`, and managing the `SearchSourceRegistry`.
- **Enable operations** register the server with the search subsystem via `try_register_server_to_search_source`, while **disable operations** remove it via `registry.remove_source()`.
- All changes propagate back to the UI through the hook's subsequent calls to `setCurrentWindowService` and `getAllServerList`, ensuring state consistency across the application.

## Frequently Asked Questions

### What happens when I call enable_server in the Coco app?

Calling **enable_server** triggers a chain that begins in [`src/hooks/useServers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/hooks/useServers.ts) and ends in the Rust backend. The command sets the server's `enabled` flag to `true`, persists the configuration to storage via `persist_servers`, and registers the server with the search source registry so it becomes available for search operations.

### How does disable_server differ from enable_server in the backend?

While **enable_server** calls `try_register_server_to_search_source` to add the server to the `SearchSourceRegistry`, **disable_server** retrieves the registry from the app state and calls `registry.remove_source(id.as_str()).await` to immediately disconnect the server from the search subsystem before persisting the disabled state.

### Where is the server connectivity state persisted?

The state is persisted in two phases: first, `save_server(&server).await` writes the individual server record, then `persist_servers(&app_handle).await` writes the complete server list to storage. Both operations occur in [`src-tauri/src/server/servers.rs`](https://github.com/infinilabs/coco-app/blob/main/src-tauri/src/server/servers.rs) after updating the `enabled` boolean.

### Can I use these commands outside of the React UI?

Yes. While the `useServers` hook provides the standard interface, you can import `platformAdapter` from [`src/utils/platformAdapter.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/platformAdapter.ts) or `commandWrapper` from [`src/utils/wrappers/tauriWrappers.ts`](https://github.com/infinilabs/coco-app/blob/main/src/utils/wrappers/tauriWrappers.ts) to invoke **enable_server** and **disable_server** directly from TypeScript utilities or other non-React contexts.