Usage Guide

Learn the fastest mental model first, then copy the service-side and edge-side patterns you need.

Fastest path

Run examples

Use chatroom for messaging and presence, or RTMP for streams.

Mental model

Services connect to :30011. Edges connect to :30012. Service -> edge usually targets a specific edgeID.

Why it feels different

Frontier uses one service-to-edge model for RPC, messaging, and streams instead of treating them as separate systems.

Start with the right example

  • Choose chatroom if you want to understand presence, messaging, and the basic long-lived connection model.
  • Choose rtmp if you care about proxying traffic, file transfer, media relay, or other point-to-point stream use cases.
  • Return to this page once you know which SDK pattern you need to copy into production code.

Using Frontier in Microservices

1. Getting the Service Client

To connect your microservice to Frontier, use the NewService method with a dialer connecting to the Service port (default: 30011).

package main

import (
    "net"
    "github.com/singchia/frontier/api/dataplane/v1/service"
)

func main() {
    dialer := func() (net.Conn, error) {
        return net.Dial("tcp", "127.0.0.1:30011")
    }
    svc, err := service.NewService(dialer)
    // Start using the service
}

2. Handling Edge Presence (Online/Offline)

You can register callbacks to know when Edge nodes connect or disconnect from the gateway.

func main() {
    // ... dialer setup ...
    svc, _ := service.NewService(dialer)

    // Register lifecycle hooks
    svc.RegisterGetEdgeID(context.TODO(), getID)
    svc.RegisterEdgeOnline(context.TODO(), online)
    svc.RegisterEdgeOffline(context.TODO(), offline)
}

// Service can assign IDs based on edge metadata
func getID(meta []byte) (uint64, error) {
    return 0, nil
}

func online(edgeID uint64, meta []byte, addr net.Addr) error {
    fmt.Printf("Edge %d is online\n", edgeID)
    return nil
}

func offline(edgeID uint64, meta []byte, addr net.Addr) error {
    fmt.Printf("Edge %d is offline\n", edgeID)
    return nil
}

3. Service to Edge RPC (Command & Control)

A Service can execute a method directly on an Edge node by its edgeID.

func main() {
    // ... dialer setup ...
    svc, _ := service.NewService(dialer)

    req := svc.NewRequest([]byte("payload data"))

    // Call the "reboot" method on Edge ID 1001
    rsp, err := svc.Call(context.TODO(), 1001, "reboot", req)
    if err != nil {
        log.Fatal(err)
    }
}

Using Frontier on Edge Nodes

1. Getting the Edge Client

Edge nodes (IoT devices, agents, clients) connect to the Edge port (default: 30012). You must provide a unique Edge ID if the Service isn't allocating them.

package main

import (
    "net"
    "github.com/singchia/frontier/api/dataplane/v1/edge"
)

func main() {
    dialer := func() (net.Conn, error) {
        return net.Dial("tcp", "127.0.0.1:30012")
    }

    // Connect with a specific Edge ID
    opt := edge.OptionEdgeID("edge-node-01")
    eg, err := edge.NewEdge(dialer, opt)
    if err != nil {
        panic(err)
    }
}

2. Edge Registers RPC for Service to Call

For the Service to be able to issue commands to the Edge, the Edge must register methods.

func main() {
    // ... edge setup ...

    // Register the "reboot" method
    eg.Register(context.TODO(), "reboot", handleReboot)
}

func handleReboot(ctx context.Context, req geminio.Request, rsp geminio.Response) {
    // Execute reboot logic
    log.Println("Received reboot command")

    // Send response back
    rsp.SetData([]byte("rebooting..."))
}

3. Edge Publishes Telemetry (Messaging)

Edges can publish data to specific Topics. Frontier routes this to Services that have subscribed to the topic, or forwards it to an external MQ like Kafka.

func main() {
    // ... edge setup ...

    msg := eg.NewMessage([]byte(`{"temp": 42.5}`))

    // Publish to the "sensor/temperature" topic
    err := eg.Publish(context.TODO(), "sensor/temperature", msg)
}

Advanced: Point-to-Point Streams

If you need to transfer large files or proxy custom protocol traffic such as SSH, VNC, or RTMP, you can open a direct multiplexed stream.

Service Opening a Stream to an Edge

func main() {
    // ... service setup ...

    // Open a new stream to Edge ID 1001.
    // st implements net.Conn, so you can use it like a raw TCP socket!
    st, err := svc.OpenStream(context.TODO(), 1001)

    // Proxy SSH traffic, relay media, or write files directly
    io.Copy(st, fileData)
}