FluidVoice LocalAPI Server Architecture and Endpoints: A Technical Deep Dive
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, 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.
LocalAPIConnectionHandler (HTTP Parsing)
For each accepted TCP connection, the server instantiates a LocalAPIConnectionHandler (lines 13–87 in 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 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): Runs the LLM-based transcription pipeline forPOST /v1/inference. - HistoryAPIController (
HistoryAPIController.swift): Manages CRUD operations for transcription logs at/v1/historyendpoints. - DictionaryAPIController (
DictionaryAPIController.swift): Handles custom pronunciation dictionaries at/v1/dictionaryendpoints.
All controllers return standardized LocalAPI.Response objects using helper factories defined in 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:
- Startup:
AppDelegatecallsLocalAPIServer.shared.start(), which readsLocalAPI.Configuration.currentto verify the feature is enabled and determine the port. - Connection Validation: The
NWListener’snewConnectionHandlervalidates the remote endpoint address, rejecting any non-loop-back connections. - Handler Instantiation: A
LocalAPIConnectionHandleris created for each accepted socket and assigned to thefluidvoice.local-apiqueue. - HTTP Parsing: The handler accumulates bytes until the header delimiter is found, then constructs a
LocalAPI.Requestinstance. - Routing: The router invokes
LocalAPIRouter.route(_:)to match the request to a controller based on path and method. - Processing: The selected controller executes business logic (often asynchronously, such as invoking the transcription engine) and returns a
LocalAPI.Response. - 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:
// 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:
curl -X POST http://127.0.0.1:4242/v1/inference \
-H "Content-Type: application/json" \
-d '{"audioBase64":"<base64-encoded-pcm>"}'
Example response:
{
"status": 200,
"data": {
"transcript": "Hello world, this is a test."
}
}
Managing Transcription History
Retrieve all stored transcriptions:
curl http://127.0.0.1:4242/v1/history
Example response:
{
"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:
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:
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:
{
"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. 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().
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →