# FluidVoice LocalAPI Server Architecture and Endpoints: A Technical Deep Dive

> Explore FluidVoice LocalAPI server architecture and endpoints. Discover its RESTful interface powered by Apple's Network framework for transcription on localhost.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: deep-dive
- Published: 2026-07-03

---

**FluidVoice’s LocalAPI server is a lightweight HTTP server built on Apple’s Network framework that exposes transcription capabilities through a RESTful interface on localhost, featuring endpoints for inference, history, and dictionary management.**

The FluidVoice macOS application from the `altic-dev/FluidVoice` repository includes a built-in LocalAPI server that enables external tools to interact with its transcription engine via standard HTTP requests. This architecture allows scripts, automation workflows, and companion applications to programmatically access speech-to-text functionality while maintaining strict security through loop-back-only networking. The implementation resides in `Sources/Fluid/Services/LocalAPI/` and employs a modular design separating transport, routing, and business logic concerns.

## Core Components of the LocalAPI Architecture

The LocalAPI server follows a layered architecture where each component handles a specific aspect of the HTTP request lifecycle.

### LocalAPIServer (TCP Listener)

The **LocalAPIServer** class, defined in [`Sources/Fluid/Services/LocalAPI/LocalAPIServer.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/LocalAPI/LocalAPIServer.swift), serves as the entry point for all API traffic. It creates an `NWListener` using Apple’s Network framework and binds exclusively to `127.0.0.1` on a configurable port (default **4242**). The server validates that every incoming connection originates from a loop-back address (`127.0.0.1`, `::1`, or `localhost`), immediately cancelling any external connection attempts. The listener instance is stored in `self.listener` and managed through `shared.start()` and `shared.stop()` methods called by [`AppDelegate.swift`](https://github.com/altic-dev/FluidVoice/blob/main/AppDelegate.swift).

### LocalAPIConnectionHandler (HTTP Parsing)

For each accepted TCP connection, the server instantiates a **LocalAPIConnectionHandler** (lines 13–87 in [`LocalAPIServer.swift`](https://github.com/altic-dev/FluidVoice/blob/main/LocalAPIServer.swift)) that runs on a dedicated utility-quality dispatch queue named `fluidvoice.local-api`. This handler buffers incoming raw bytes until it detects the HTTP header delimiter (`\r\n\r\n`), then parses the method, target path, query string, headers, and optional body into a `LocalAPI.Request` struct. After processing, it serializes the returned `LocalAPI.Response` into a valid HTTP/1.1 response with `Content-Length` and `Connection: close` headers before terminating the socket.

### LocalAPIRouter (Request Dispatching)

The **LocalAPIRouter** in [`Sources/Fluid/Services/LocalAPI/LocalAPIRouter.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/LocalAPI/LocalAPIRouter.swift) implements a thin routing layer that examines the HTTP method and path, then dispatches to the appropriate controller via a `switch` statement. Unknown routes return a 404 Not Found response. The router handles the `/v1` namespace and delegates to specific controllers based on the second path component (`inference`, `history`, or `dictionary`).

### Controllers (Business Logic)

Three specialized controllers implement the endpoint logic:

- **InferenceAPIController** ([`InferenceAPIController.swift`](https://github.com/altic-dev/FluidVoice/blob/main/InferenceAPIController.swift)): Runs the LLM-based transcription pipeline for `POST /v1/inference`.
- **HistoryAPIController** ([`HistoryAPIController.swift`](https://github.com/altic-dev/FluidVoice/blob/main/HistoryAPIController.swift)): Manages CRUD operations for transcription logs at `/v1/history` endpoints.
- **DictionaryAPIController** ([`DictionaryAPIController.swift`](https://github.com/altic-dev/FluidVoice/blob/main/DictionaryAPIController.swift)): Handles custom pronunciation dictionaries at `/v1/dictionary` endpoints.

All controllers return standardized `LocalAPI.Response` objects using helper factories defined in [`LocalAPIModels.swift`](https://github.com/altic-dev/FluidVoice/blob/main/LocalAPIModels.swift), such as `LocalAPI.ok(_:)` and `LocalAPI.error(_:status:)`.

## Request Flow Through the LocalAPI Server

Understanding the exact request lifecycle helps debug integration issues and optimize client implementations:

1. **Startup**: `AppDelegate` calls `LocalAPIServer.shared.start()`, which reads `LocalAPI.Configuration.current` to verify the feature is enabled and determine the port.
2. **Connection Validation**: The `NWListener`’s `newConnectionHandler` validates the remote endpoint address, rejecting any non-loop-back connections.
3. **Handler Instantiation**: A `LocalAPIConnectionHandler` is created for each accepted socket and assigned to the `fluidvoice.local-api` queue.
4. **HTTP Parsing**: The handler accumulates bytes until the header delimiter is found, then constructs a `LocalAPI.Request` instance.
5. **Routing**: The router invokes `LocalAPIRouter.route(_:)` to match the request to a controller based on path and method.
6. **Processing**: The selected controller executes business logic (often asynchronously, such as invoking the transcription engine) and returns a `LocalAPI.Response`.
7. **Response Transmission**: The connection handler serializes the response to HTTP/1.1 format, writes the bytes to the socket, and closes the connection.

## LocalAPI Endpoints Reference

The server exposes a RESTful API under the `/v1` namespace with JSON request/response formats. All responses follow the envelope structure `{ "status": <int>, "data": ... }` for success or `{ "status": <int>, "error": "..." }` for failures.

| Method | Path | Controller | Description |
|--------|------|------------|-------------|
| **POST** | `/v1/inference` | `InferenceAPIController` | Accepts JSON payload with `audioBase64` (base64-encoded PCM) or file reference, runs transcription, returns transcribed text. |
| **GET** | `/v1/history` | `HistoryAPIController` | Returns JSON array of past transcriptions with `id`, `timestamp`, and `transcript` fields. |
| **GET** | `/v1/history/{id}` | `HistoryAPIController` | Fetches single history entry by unique identifier. |
| **DELETE** | `/v1/history/{id}` | `HistoryAPIController` | Removes specific transcription entry from local store. |
| **GET** | `/v1/dictionary` | `DictionaryAPIController` | Returns current custom dictionary as JSON map of words to phonetic strings. |
| **POST** | `/v1/dictionary` | `DictionaryAPIController` | Adds or updates dictionary entries; payload is JSON map of word → pronunciation. |
| **DELETE** | `/v1/dictionary/{word}` | `DictionaryAPIController` | Deletes specific word from custom dictionary. |

HTTP status codes follow standard semantics: **200 OK**, **204 No Content**, **400 Bad Request**, **404 Not Found**, **405 Method Not Allowed**, **413 Payload Too Large**, and **500 Internal Error**.

## Working with the LocalAPI Server

### Server Initialization

The server starts automatically when the FluidVoice app launches. Manual control is available through the shared singleton:

```swift
// In AppDelegate.swift
LocalAPIServer.shared.start()

```

Configuration toggles (enabled state and port) are read from `LocalAPI.Configuration.current` before binding.

### Transcription Inference

Submit audio data for transcription using the inference endpoint:

```bash
curl -X POST http://127.0.0.1:4242/v1/inference \
     -H "Content-Type: application/json" \
     -d '{"audioBase64":"<base64-encoded-pcm>"}'

```

**Example response:**

```json
{
  "status": 200,
  "data": {
    "transcript": "Hello world, this is a test."
  }
}

```

### Managing Transcription History

Retrieve all stored transcriptions:

```bash
curl http://127.0.0.1:4242/v1/history

```

**Example response:**

```json
{
  "status": 200,
  "data": [
    {
      "id": "2024-09-01T12-34-56Z",
      "timestamp": "2024-09-01T12:34:56Z",
      "transcript": "First line of text."
    }
  ]
}

```

Delete a specific entry by ID:

```bash
curl -X DELETE http://127.0.0.1:4242/v1/history/2024-09-01T12-34-56Z

```

### Updating the Custom Dictionary

Add pronunciation overrides for domain-specific terminology:

```bash
curl -X POST http://127.0.0.1:4242/v1/dictionary \
     -H "Content-Type: application/json" \
     -d '{"FluidVoice":"ˈfluːɪd ˈvɔɪs", "AI":"ˈeɪ aɪ"}'

```

**Example response:**

```json
{
  "status": 200,
  "data": { "updated": true }
}

```

## Summary

- **FluidVoice’s LocalAPI server** uses Apple’s Network framework (`NWListener`) to provide a lightweight HTTP server bound exclusively to localhost.
- **Security** is enforced at the connection level by rejecting any non-loop-back addresses (127.0.0.1, ::1, localhost).
- **Modular architecture** separates concerns across `LocalAPIServer` (transport), `LocalAPIConnectionHandler` (protocol), `LocalAPIRouter` (dispatching), and specialized controllers (business logic).
- **RESTful endpoints** expose transcription capabilities (`/v1/inference`), historical data management (`/v1/history`), and custom pronunciation dictionaries (`/v1/dictionary`).
- **Default configuration** uses port 4242 with JSON request/response formats, configurable via `LocalAPI.Configuration.current`.

## Frequently Asked Questions

### What networking framework does FluidVoice use for the LocalAPI server?

The server is built on Apple’s **Network framework**, utilizing `NWListener` for TCP socket management and `NWConnection` for per-client handling. This provides modern asynchronous I/O with automatic back-pressure management and integrated TLS support if needed in future versions.

### Can external machines access the LocalAPI server over the network?

No. The server explicitly validates that every incoming connection originates from a loop-back address (`127.0.0.1`, `::1`, or `localhost`). Any connection attempt from external IP addresses is immediately cancelled in the `newConnectionHandler`, ensuring the API remains strictly local to the macOS host machine.

### How does the LocalAPI server handle concurrent requests?

Each TCP connection spawns an independent **LocalAPIConnectionHandler** instance running on a dedicated utility-quality dispatch queue named `fluidvoice.local-api`. Since the implementation uses HTTP/1.1 with `Connection: close` semantics (one request per connection), concurrent requests are processed in parallel through separate handler instances without blocking the main listener thread.

### Where is the default port configured, and can it be changed?

The default port **4242** is defined as `LocalAPI.defaultPort` in [`LocalAPIModels.swift`](https://github.com/altic-dev/FluidVoice/blob/main/LocalAPIModels.swift). Users can override this value through the app’s preferences UI, which updates `LocalAPI.Configuration.current.port` before the server binds to the socket in `LocalAPIServer.shared.start()`.