Back to projects

In development · expected 2026.12

Lingding

A language-exchange app that matches Korean and Japanese learners and connects them through one-to-one voice calls.

A language-exchange app where Korean and Japanese learners find a partner and continue into real conversation. I own the one-to-one call feature, Go WebSocket signaling server, ICE server setup, and basic CRUD API design.

Period
2026.05 - Present
Ownership
One-to-one calls · signaling server · ICE server · CRUD design
Team
Blueberry Team
Repository
Private repository

Connecting a matched pair to a real conversation

Lingding helps Korean and Japanese learners find a partner and learn through conversation. Profiles and matching alone did not start an exchange, so the product needed a direct path into a one-to-one voice call.

I owned the call feature, the Go signaling server, the ICE server setup that supports different network environments, and the design of the service's basic CRUD APIs.

One-to-one call

The server coordinates the two people while audio travels between devices

  1. 01

    Caller

    First phone

  2. 02

    Call coordinator

    Go WebSocket signaling

  3. 03

    Find a connection path

    STUN first, TURN if needed

  4. 04

    Callee

    Second phone

  5. 05

    Audio path

    WebRTC direct or through TURN

CRUD requests and calls do not live on the same timeline

Profile and matching CRUD ends after a response, but a call must retain the room, two WebSockets, and the order of offer, answer, and ICE candidates until hang-up.

Across two clients, room entry, offer, answer, and ICE candidates can arrive in different orders. Missing peers, duplicate connections, and messages after disconnect also needed handling beyond normal request errors.

I kept regular data operations in HTTP CRUD and managed room and signaling events through Go WebSockets. Explicit message types and a room lifecycle made the server responsible for keeping both clients on the same call state.

  • Managed room join, leave, and both peer states on the server.
  • Relayed SDP offer/answer and ICE candidates to the other peer.
  • Cleaned up room and connection resources after disconnect.

I implemented room entry, signaling relay, and disconnect cleanup in the Go WebSocket server and connected that path to the Flutter one-to-one call flow. Concurrent connections and event latency have not yet been measured in the real environment.

A real-time server requires explicit state, event ordering, and cleanup across the whole connection rather than one request at a time.

Lingding promotional image

Issuing short-lived TURN credentials from the server

Phones behind different NAT or firewall conditions may need a TURN relay when a direct connection fails. Embedding a fixed credential in the mobile app would leave it extractable and reusable after distribution.

The initial requirement was one-to-one audio, so a managed service fit the scope better than operating a custom TURN server. Credential issuance and cost control, however, could not be left to the client.

I preferred a direct path discovered through STUN and used Cloudflare TURN as a fallback. Instead of fixing TURN credentials in the app, the server issues short-lived credentials through the Cloudflare API.

  • Implemented a short-lived TURN credential endpoint in the Go server.
  • Applied issued credentials to the Flutter client's ICE server configuration.
  • Separated signaling responsibility from media-relay credentials.

Short-lived issuance and client application are implemented. TURN usage by network type, credential rate limiting, and cost are not yet validated, so this is presented as an implementation result rather than an operational outcome.

I learned that client-facing secrets need a server-managed lifetime, issuer, and scope—not only concealment.

Lingding rough home screen

The implementation includes Flutter one-to-one audio, a Go WebSocket signaling server, ICE server setup with STUN/TURN, and basic CRUD API design.

CRUD APIs taught me to think per request; real-time work taught me that the server must continuously manage connection state, event order, and cleanup after disconnect.

I have not yet measured load, connection success, latency, or long-call behavior in Lingding's real environment, and there are no real-user metrics. December 2026 is a target, not a confirmed release date.

  • Build a Wi-Fi/LTE/5G/restricted-network connection matrix
  • Measure rooms, WebSocket counts, and offer → answer → ICE completion
  • Verify credential TTL, rate limits, reconnects, and long-call soak tests