docs: reposition frontier and add website (#99)

* docs: reposition frontier and add website

* docs: make readme more visual

* docs: remove duplicated readme title

* docs: simplify readme and move details to docs

* docs: align chinese readme with new structure

* docs: soften readme section titles

* chore: trigger vercel preview
This commit is contained in:
Austin Zhai
2026-04-16 16:51:05 +08:00
committed by GitHub
parent f420773b0e
commit 211cedaca6
46 changed files with 9717 additions and 1227 deletions
+1
View File
@@ -34,3 +34,4 @@ bin/
# tmp file on linux,
*swp
*~
.vercel
+8
View File
@@ -0,0 +1,8 @@
.git
.playwright-mcp
website/node_modules
website/.next
website/screenshots
website/.vercel
website/.env*
**/.DS_Store
+146 -397
View File
@@ -13,39 +13,143 @@ English | [简体中文](./README_zh.md)
</div>
> Bidirectional service-to-edge gateway for long-lived connections
Frontier is a **full-duplex**, open-source long-connection gateway written in Go. It enables microservices to directly reach edge nodes or clients, and vice versa. It provides full-duplex **bidirectional RPC**, **messaging**, and **point-to-point streams**. Frontier follows **cloud-native** architecture principles, supports fast cluster deployment via Operator, and is built for **high availability** and **elastic scaling** to millions of online edge nodes or clients.
Frontier is an open-source gateway written in Go for **service <-> edge communication**. It lets backend services and edge nodes talk to each other over long-lived connections, with built-in **bidirectional RPC**, **messaging**, and **point-to-point streams**.
It is built for systems where both sides stay online and need to actively call, notify, or open streams to each other. Frontier is **not a reverse proxy** and **not just a message broker**. It is infrastructure for addressing and operating large fleets of connected edge nodes from backend services.
<p align="center">
<img src="./docs/diagram/frontier.png" width="100%" alt="Frontier architecture overview">
</p>
## What Makes Frontier Different
- **Different from API gateways**: Frontier is designed for backend-to-edge communication, not just north-south HTTP traffic.
- **Different from MQ**: It gives you bidirectional RPC, messaging, and streams in one connectivity model.
- **Different from tunnels**: Services can address a specific online edge node instead of only exposing a port.
- **Made for real fleets**: Works for devices, agents, clients, and remote connectors that stay online for a long time.
## What You Can Build
<table>
<tr>
<td width="50%">
<img src="./docs/diagram/rtmp.png" alt="Frontier stream relay example">
<p><strong>Traffic relay and media streaming</strong><br>Open point-to-point streams for RTMP relay, file transfer, proxy traffic, or other custom protocols.</p>
</td>
<td width="50%">
<img src="./docs/diagram/stream.png" alt="Frontier stream architecture">
<p><strong>Remote agents and device fleets</strong><br>Keep edge nodes online, route service calls to a specific edge, and let the edge call backend services back.</p>
</td>
</tr>
</table>
## At A Glance
| You need to... | Frontier gives you... |
| --- | --- |
| Call a specific online device or agent from your backend | Service -> Edge RPC and messaging over long-lived connections |
| Let edge nodes initiate calls without opening inbound ports | Edge -> Service RPC on the same connection model |
| Move bytes, not just request/response payloads | Point-to-point streams between service and edge |
| Run one control plane for a large connected fleet | Presence, lifecycle hooks, control APIs, clustering |
## Table of Contents
- [Features](#features)
- [Why Frontier](#why-frontier)
- [What Makes Frontier Different](#what-makes-frontier-different)
- [What You Can Build](#what-you-can-build)
- [At A Glance](#at-a-glance)
- [When to Use Frontier](#when-to-use-frontier)
- [Real-World Use Cases](#real-world-use-cases)
- [Comparison](#comparison)
- [Quick Start](#quick-start)
- [Architecture](#architecture)
- [Usage](#usage)
- [Configuration](#configuration)
- [Deployment](#deployment)
- [Cluster](#cluster)
- [Kubernetes](#kubernetes)
- [Development](#development)
- [Testing](#testing)
- [Docs](#docs)
- [Community](#community)
- [License](#license)
## Why Frontier
Most infrastructure is optimized for one of these models:
- **service -> service** via HTTP or gRPC
- **client -> service** via API gateways and reverse proxies
- **event fan-out** via message brokers
Frontier is optimized for a different model:
- **service <-> edge** over long-lived, stateful connections
- backend services calling a specific online edge node
- edge nodes calling backend services without exposing inbound ports
- opening direct streams between services and edge nodes when RPC is not enough
<p align="center">
<img src="./docs/diagram/frontlas.png" width="88%" alt="Frontier clustering with Frontlas">
</p>
## When to Use Frontier
Use Frontier if you need:
- Backend services to call specific online devices, agents, clients, or connectors
- Edge nodes to call backend services over the same connectivity model
- Long-lived connections at large scale
- One data plane for RPC, messaging, and streams
- Cluster deployment and high availability for service-to-edge connectivity
Do not use Frontier if:
- You only need service-to-service RPC; gRPC is a simpler fit
- You only need HTTP ingress, routing, or proxying; use an API gateway or Envoy
- You only need pub/sub or event streaming; use NATS or Kafka
- You only need a generic tunnel; use frp or another tunneling tool
## Real-World Use Cases
- IoT and device fleets
- Remote agents and connectors
- IM and other real-time systems
- Game backends talking to online clients or edge nodes
- Zero-trust internal access based on connector-style agents
- File transfer, media relay, or traffic proxy over point-to-point streams
## Use Cases In One Screen
| Scenario | Why Frontier fits |
| --- | --- |
| Device control plane | Address a specific online edge node, push commands, receive state, and keep the link alive |
| Remote connector platform | Let connectors dial out, avoid inbound exposure, and keep service-side routing simple |
| Real-time apps | Maintain long-lived sessions and combine notifications, RPC, and streams in one path |
| Internal zero-trust access | Use agent-style edges as the last-mile bridge between backend systems and private resources |
## Comparison
| Capability | Frontier | gRPC | NATS | frp | Envoy |
| --- | --- | --- | --- | --- | --- |
| Built for service <-> edge communication | Yes | No | Partial | No | No |
| Backend can address a specific online edge node | Yes | No | Partial | Partial | No |
| Edge can call backend services | Yes | Partial | Yes | No | No |
| Point-to-point streams between service and edge | Yes | Partial | No | Tunnel only | No |
| Unified RPC + messaging + streams model | Yes | No | No | No | No |
| Long-lived connection fleet as a primary model | Yes | No | Partial | Partial | No |
`Partial` here means the capability can be assembled with extra patterns, but it is not the system's primary communication model.
## Quick Start
1. Run a single Frontier instance:
1. Start a single Frontier instance:
```bash
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2
```
2. Build and run examples:
2. Build the examples:
```bash
make examples
```
Run the chatroom example:
3. Run the chatroom demo:
```bash
# Terminal 1
@@ -55,398 +159,43 @@ Run the chatroom example:
./bin/chatroom_agent
```
The chatroom example shows the basic Frontier interaction model: long-lived connectivity, edge online/offline events, and service <-> edge messaging.
You can also run the RTMP example if you want to see Frontier's stream model used for traffic relay:
```bash
# Terminal 1
./bin/rtmp_service
# Terminal 2
./bin/rtmp_edge
```
Demo video:
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
## Features
## Docs
- **Bidirectional RPC**: Services and edges can call each other with load balancing.
- **Messaging**: Topic-based publish/receive between services, edges, and external MQ.
- **Point-to-Point Streams**: Open direct streams for proxying, file transfer, and custom traffic.
- **Cloud-Native Deployment**: Run via Docker, Compose, Helm, or Operator.
- **High Availability and Scaling**: Support for reconnect, clustering, and horizontal scale with Frontlas.
- **Auth and Presence**: Edge auth and online/offline notifications.
- **Control Plane APIs**: gRPC and REST APIs for querying and managing online nodes.
README is intentionally optimized for fast understanding and fast conversion. The implementation details, configuration surface, deployment playbooks, and cluster operations live in the docs.
Start here:
## Architecture
- [Usage Guide](./docs/USAGE.md)
- [Configuration Guide](./docs/CONFIGURATION.md)
- [Technical Docs Index](./docs/README.md)
- [Systemd Deployment](./dist/systemd/README.md)
- [Docker Compose Deployment](./dist/compose/README.md)
- [Helm and Operator Deployment](./dist/helm/README.md)
- [Roadmap](./ROADMAP.md)
**Frontier Component**
If you are evaluating Frontier for production, the docs cover:
<img src="./docs/diagram/frontier.png" width="100%">
- _Service End_: The entry point for microservice functions, connecting by default.
- _Edge End_: The entry point for edge node or client functions.
- _Publish/Receive_: Publishing and receiving messages.
- _Call/Register_: Calling and registering functions.
- _OpenStream/AcceptStream_: Opening and accepting point-to-point streams (connections).
- _External MQ_: Frontier supports forwarding messages published from edge nodes to external MQ topics based on configuration.
Frontier requires both microservices and edge nodes to actively connect to Frontier. The metadata of Service and Edge (receiving topics, RPC, service names, etc.) can be carried during the connection. The default connection ports are:
- :30011: For microservices to connect and obtain Service.
- :30012: For edge nodes to connect and obtain Edge.
- :30010: For operations personnel or programs to use the control plane.
### Functionality
<table><thead>
<tr>
<th>Function</th>
<th>Initiator</th>
<th>Receiver</th>
<th>Method</th>
<th>Routing Method</th>
<th>Description</th>
</tr></thead>
<tbody>
<tr>
<td rowspan="2">Messager</td>
<td>Service</td>
<td>Edge</td>
<td>Publish</td>
<td>EdgeID+Topic</td>
<td>Must publish to a specific EdgeID, the default topic is empty. The edge calls Receive to receive the message, and after processing, must call msg.Done() or msg.Error(err) to ensure message consistency.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service or External MQ</td>
<td>Publish</td>
<td>Topic</td>
<td>Must publish to a topic, and Frontier selects a specific Service or MQ based on the topic.</td>
</tr>
<tr>
<td rowspan="2">RPCer</td>
<td>Service</td>
<td>Edge</td>
<td>Call</td>
<td>EdgeID+Method</td>
<td>Must call a specific EdgeID, carrying the method name.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>Call</td>
<td>Method</td>
<td>Must call a method, and Frontier selects a specific Service based on the method name.</td>
</tr>
<tr>
<td rowspan="2">Multiplexer</td>
<td>Service</td>
<td>Edge</td>
<td>OpenStream</td>
<td>EdgeID</td>
<td>Must open a stream to a specific EdgeID.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>OpenStream</td>
<td>ServiceName</td>
<td>Must open a stream to a ServiceName, specified by service.OptionServiceName during Service initialization.</td>
</tr>
</tbody></table>
**Key design principles include**:
1. All messages, RPCs, and Streams are point-to-point transmissions.
- From microservices to edges, the edge node ID must be specified.
- From edges to microservices, Frontier routes based on Topic and Method, and finally selects a microservice or external MQ through hashing. The default is hashing based on edgeid, but you can choose random or srcip.
2. Messages require explicit acknowledgment by the receiver.
- To ensure message delivery semantics, the receiver must call msg.Done() or msg.Error(err) to ensure delivery consistency.
3. Streams opened by the Multiplexer logically represent direct communication between microservices and edge nodes.
- Once the other side receives the stream, all functionalities on this stream will directly reach the other side, bypassing Frontier's routing policies.
## Usage
Detailed usage guide: [docs/USAGE.md](./docs/USAGE.md)
## Configuration
Detailed configuration guide: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
## Deployment
In a single Frontier instance, you can choose the following methods to deploy your Frontier instance based on your environment.
### Docker
```bash
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
```
### Docker-Compose
```bash
git clone https://github.com/singchia/frontier.git
cd dist/compose
docker-compose up -d frontier
```
### Helm
If you are in a Kubernetes environment, you can use Helm to quickly deploy an instance.
```bash
git clone https://github.com/singchia/frontier.git
cd dist/helm
helm install frontier ./ -f values.yaml
```
Your microservice should connect to ```service/frontier-servicebound-svc:30011```, and your edge node can connect to the NodePort where `:30012` is located.
### Systemd
Use the dedicated Systemd docs:
[dist/systemd/README.md](./dist/systemd/README.md)
### Operator
See the cluster deployment section below.
## Cluster
### Frontier + Frontlas Architecture
<img src="./docs/diagram/frontlas.png" width="100%">
The additional Frontlas component is used to build the cluster. Frontlas is also a stateless component and does not store other information in memory, so it requires additional dependency on Redis. You need to provide a Redis connection information to Frontlas, supporting `redis`, `sentinel`, and `redis-cluster`.
- _Frontier_: Communication component between microservices and edge data planes.
- _Frontlas_: Named Frontier Atlas, a cluster management component that records metadata and active information of microservices and edges in Redis.
Frontier needs to proactively connect to Frontlas to report its own, microservice, and edge active and status. The default ports for Frontlas are:
- `:40011` for microservices connection, replacing the 30011 port in a single Frontier instance.
- `:40012` for Frontier connection to report status.
You can deploy any number of Frontier instances as needed, and for Frontlas, deploying two instances separately can ensure HA (High Availability) since it does not store state and has no consistency issues.
### Configuration
**Frontier**'s `frontier.yaml` needs to add the following configuration:
```yaml
frontlas:
enable: true
dial:
network: tcp
addr:
- 127.0.0.1:40012
metrics:
enable: false
interval: 0
daemon:
# Unique ID within the Frontier cluster
frontier_id: frontier01
```
Frontier needs to connect to Frontlas to report its own, microservice, and edge active and status.
**Frontlas**'s `frontlas.yaml` minimal configuration:
```yaml
control_plane:
listen:
# Microservices connect to this address to discover edges in the cluster
network: tcp
addr: 0.0.0.0:40011
frontier_plane:
# Frontier connects to this address
listen:
network: tcp
addr: 0.0.0.0:40012
expiration:
# Expiration time for microservice metadata in Redis
service_meta: 30
# Expiration time for edge metadata in Redis
edge_meta: 30
redis:
# Support for standalone, sentinel, and cluster connections
mode: standalone
standalone:
network: tcp
addr: redis:6379
db: 0
```
### Usage
Since Frontlas is used to discover available Frontiers, microservices need to adjust as follows:
**Microservice Getting Service**
```golang
package main
import (
"net"
"github.com/singchia/frontier/api/dataplane/v1/service"
)
func main() {
// Use NewClusterService to get Service
svc, err := service.NewClusterService("127.0.0.1:40011")
// Start using service, everything else remains unchanged
}
```
**Edge Node Getting Connection Address**
For edge nodes, they still connect to Frontier but can get available Frontier addresses from Frontlas. Frontlas provides an interface to list Frontier instances:
```bash
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
```
You can wrap this interface to provide load balancing or high availability for edge nodes, or add mTLS to directly provide to edge nodes (not recommended).
Control Plane gRPC See [Protobuf Definition](./api/controlplane/frontlas/v1/cluster.proto).
The Frontlas control plane differs from Frontier as it is a cluster-oriented control plane, currently providing only read interfaces for the cluster.
```protobuf
service ClusterService {
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
}
```
## Kubernetes
### Operator
**Install CRD and Operator**
Follow these steps to install and deploy the Operator to your .kubeconfig environment:
```bash
git clone https://github.com/singchia/frontier.git
cd dist/crd
kubectl apply -f install.yaml
```
Check CRD:
```bash
kubectl get crd frontierclusters.frontier.singchia.io
```
Check Operator:
```bash
kubectl get all -n frontier-system
```
**FrontierCluster**
```yaml
apiVersion: frontier.singchia.io/v1alpha1
kind: FrontierCluster
metadata:
labels:
app.kubernetes.io/name: frontiercluster
app.kubernetes.io/managed-by: kustomize
name: frontiercluster
spec:
frontier:
# Single instance Frontier
replicas: 2
# Microservice side port
servicebound:
port: 30011
# Edge node side port
edgebound:
port: 30012
frontlas:
# Single instance Frontlas
replicas: 1
# Control plane port
controlplane:
port: 40011
redis:
# Dependent Redis configuration
addrs:
- rfs-redisfailover:26379
password: your-password
masterName: mymaster
redisType: sentinel
```
Save as`frontiercluster.yaml`and
```
kubectl apply -f frontiercluster.yaml
```
In 1 minute, you will have a 2-instance Frontier + 1-instance Frontlas cluster.
Check resource deployment status with:
```bash
kubectl get all -l app=frontiercluster-frontier
kubectl get all -l app=frontiercluster-frontlas
```
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
```
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
```
Your microservice should connect to `service/frontiercluster-frontlas-svc:40011`, and your edge node can connect to the NodePort where `:30012` is located.
## Development
### Roadmap
See [ROADMAP](./ROADMAP.md)
### Contributions
If you find any bugs, please open an issue, and project maintainers will respond promptly.
If you wish to submit features or more quickly address project issues, you are welcome to submit PRs under these simple conditions:
- Code style remains consistent
- Each submission includes one feature
- Submitted code includes unit tests
## Testing
### Stream Function
<img src="./docs/diagram/stream.png" width="100%">
- architecture and communication model
- RPC, messaging, and point-to-point stream semantics
- deployment on Docker, Compose, Helm, and Operator
- cluster mode with Frontlas and Redis
- development workflow and contribution guidelines
## Community
+142 -393
View File
@@ -13,41 +13,143 @@ English | [简体中文](./README_zh.md)
</div>
> Bidirectional service-to-edge gateway for long-lived connections
# Frontier
Frontier is an open-source gateway written in Go for **service <-> edge communication**. It lets backend services and edge nodes talk to each other over long-lived connections, with built-in **bidirectional RPC**, **messaging**, and **point-to-point streams**.
Frontier is a full-duplex, open-source long-connection gateway written in Go. It enables microservices to directly reach edge nodes or clients, and vice versa. It provides full-duplex bidirectional RPC, messaging, and point-to-point streams. Frontier follows cloud-native architecture principles, supports fast cluster deployment via Operator, and is built for high availability and elastic scaling to millions of online edge nodes or clients.
It is built for systems where both sides stay online and need to actively call, notify, or open streams to each other. Frontier is **not a reverse proxy** and **not just a message broker**. It is infrastructure for addressing and operating large fleets of connected edge nodes from backend services.
<p align="center">
<img src="./docs/diagram/frontier.png" width="100%" alt="Frontier architecture overview">
</p>
## What Makes Frontier Different
- **Different from API gateways**: Frontier is designed for backend-to-edge communication, not just north-south HTTP traffic.
- **Different from MQ**: It gives you bidirectional RPC, messaging, and streams in one connectivity model.
- **Different from tunnels**: Services can address a specific online edge node instead of only exposing a port.
- **Made for real fleets**: Works for devices, agents, clients, and remote connectors that stay online for a long time.
## What You Can Build
<table>
<tr>
<td width="50%">
<img src="./docs/diagram/rtmp.png" alt="Frontier stream relay example">
<p><strong>Traffic relay and media streaming</strong><br>Open point-to-point streams for RTMP relay, file transfer, proxy traffic, or other custom protocols.</p>
</td>
<td width="50%">
<img src="./docs/diagram/stream.png" alt="Frontier stream architecture">
<p><strong>Remote agents and device fleets</strong><br>Keep edge nodes online, route service calls to a specific edge, and let the edge call backend services back.</p>
</td>
</tr>
</table>
## At A Glance
| You need to... | Frontier gives you... |
| --- | --- |
| Call a specific online device or agent from your backend | Service -> Edge RPC and messaging over long-lived connections |
| Let edge nodes initiate calls without opening inbound ports | Edge -> Service RPC on the same connection model |
| Move bytes, not just request/response payloads | Point-to-point streams between service and edge |
| Run one control plane for a large connected fleet | Presence, lifecycle hooks, control APIs, clustering |
## Table of Contents
- [Features](#features)
- [Why Frontier](#why-frontier)
- [What Makes Frontier Different](#what-makes-frontier-different)
- [What You Can Build](#what-you-can-build)
- [At A Glance](#at-a-glance)
- [When to Use Frontier](#when-to-use-frontier)
- [Real-World Use Cases](#real-world-use-cases)
- [Comparison](#comparison)
- [Quick Start](#quick-start)
- [Architecture](#architecture)
- [Usage](#usage)
- [Configuration](#configuration)
- [Deployment](#deployment)
- [Cluster](#cluster)
- [Kubernetes](#kubernetes)
- [Development](#development)
- [Testing](#testing)
- [Docs](#docs)
- [Community](#community)
- [License](#license)
## Why Frontier
Most infrastructure is optimized for one of these models:
- **service -> service** via HTTP or gRPC
- **client -> service** via API gateways and reverse proxies
- **event fan-out** via message brokers
Frontier is optimized for a different model:
- **service <-> edge** over long-lived, stateful connections
- backend services calling a specific online edge node
- edge nodes calling backend services without exposing inbound ports
- opening direct streams between services and edge nodes when RPC is not enough
<p align="center">
<img src="./docs/diagram/frontlas.png" width="88%" alt="Frontier clustering with Frontlas">
</p>
## When to Use Frontier
Use Frontier if you need:
- Backend services to call specific online devices, agents, clients, or connectors
- Edge nodes to call backend services over the same connectivity model
- Long-lived connections at large scale
- One data plane for RPC, messaging, and streams
- Cluster deployment and high availability for service-to-edge connectivity
Do not use Frontier if:
- You only need service-to-service RPC; gRPC is a simpler fit
- You only need HTTP ingress, routing, or proxying; use an API gateway or Envoy
- You only need pub/sub or event streaming; use NATS or Kafka
- You only need a generic tunnel; use frp or another tunneling tool
## Real-World Use Cases
- IoT and device fleets
- Remote agents and connectors
- IM and other real-time systems
- Game backends talking to online clients or edge nodes
- Zero-trust internal access based on connector-style agents
- File transfer, media relay, or traffic proxy over point-to-point streams
## Use Cases In One Screen
| Scenario | Why Frontier fits |
| --- | --- |
| Device control plane | Address a specific online edge node, push commands, receive state, and keep the link alive |
| Remote connector platform | Let connectors dial out, avoid inbound exposure, and keep service-side routing simple |
| Real-time apps | Maintain long-lived sessions and combine notifications, RPC, and streams in one path |
| Internal zero-trust access | Use agent-style edges as the last-mile bridge between backend systems and private resources |
## Comparison
| Capability | Frontier | gRPC | NATS | frp | Envoy |
| --- | --- | --- | --- | --- | --- |
| Built for service <-> edge communication | Yes | No | Partial | No | No |
| Backend can address a specific online edge node | Yes | No | Partial | Partial | No |
| Edge can call backend services | Yes | Partial | Yes | No | No |
| Point-to-point streams between service and edge | Yes | Partial | No | Tunnel only | No |
| Unified RPC + messaging + streams model | Yes | No | No | No | No |
| Long-lived connection fleet as a primary model | Yes | No | Partial | Partial | No |
`Partial` here means the capability can be assembled with extra patterns, but it is not the system's primary communication model.
## Quick Start
1. Run a single Frontier instance:
1. Start a single Frontier instance:
```bash
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2
```
2. Build and run examples:
2. Build the examples:
```bash
make examples
```
Run the chatroom example:
3. Run the chatroom demo:
```bash
# Terminal 1
@@ -57,396 +159,43 @@ Run the chatroom example:
./bin/chatroom_agent
```
Demo video: https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
The chatroom example shows the basic Frontier interaction model: long-lived connectivity, edge online/offline events, and service <-> edge messaging.
## Features
- **Bidirectional RPC**: Services and edges can call each other with load balancing.
- **Messaging**: Topic-based publish/receive between services, edges, and external MQ.
- **Point-to-Point Streams**: Open direct streams for proxying, file transfer, and custom traffic.
- **Cloud-Native Deployment**: Run via Docker, Compose, Helm, or Operator.
- **High Availability and Scaling**: Support for reconnect, clustering, and horizontal scale with Frontlas.
- **Auth and Presence**: Edge auth and online/offline notifications.
- **Control Plane APIs**: gRPC and REST APIs for querying and managing online nodes.
## Architecture
**Frontier Component**
<img src="./docs/diagram/frontier.png" width="100%">
- _Service End_: The entry point for microservice functions, connecting by default.
- _Edge End_: The entry point for edge node or client functions.
- _Publish/Receive_: Publishing and receiving messages.
- _Call/Register_: Calling and registering functions.
- _OpenStream/AcceptStream_: Opening and accepting point-to-point streams (connections).
- _External MQ_: Frontier supports forwarding messages published from edge nodes to external MQ topics based on configuration.
Frontier requires both microservices and edge nodes to actively connect to Frontier. The metadata of Service and Edge (receiving topics, RPC, service names, etc.) can be carried during the connection. The default connection ports are:
- :30011: For microservices to connect and obtain Service.
- :30012: For edge nodes to connect and obtain Edge.
- :30010: For operations personnel or programs to use the control plane.
### Functionality
<table><thead>
<tr>
<th>Function</th>
<th>Initiator</th>
<th>Receiver</th>
<th>Method</th>
<th>Routing Method</th>
<th>Description</th>
</tr></thead>
<tbody>
<tr>
<td rowspan="2">Messager</td>
<td>Service</td>
<td>Edge</td>
<td>Publish</td>
<td>EdgeID+Topic</td>
<td>Must publish to a specific EdgeID, the default topic is empty. The edge calls Receive to receive the message, and after processing, must call msg.Done() or msg.Error(err) to ensure message consistency.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service or External MQ</td>
<td>Publish</td>
<td>Topic</td>
<td>Must publish to a topic, and Frontier selects a specific Service or MQ based on the topic.</td>
</tr>
<tr>
<td rowspan="2">RPCer</td>
<td>Service</td>
<td>Edge</td>
<td>Call</td>
<td>EdgeID+Method</td>
<td>Must call a specific EdgeID, carrying the method name.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>Call</td>
<td>Method</td>
<td>Must call a method, and Frontier selects a specific Service based on the method name.</td>
</tr>
<tr>
<td rowspan="2">Multiplexer</td>
<td>Service</td>
<td>Edge</td>
<td>OpenStream</td>
<td>EdgeID</td>
<td>Must open a stream to a specific EdgeID.</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>OpenStream</td>
<td>ServiceName</td>
<td>Must open a stream to a ServiceName, specified by service.OptionServiceName during Service initialization.</td>
</tr>
</tbody></table>
**Key design principles include**:
1. All messages, RPCs, and Streams are point-to-point transmissions.
- From microservices to edges, the edge node ID must be specified.
- From edges to microservices, Frontier routes based on Topic and Method, and finally selects a microservice or external MQ through hashing. The default is hashing based on edgeid, but you can choose random or srcip.
2. Messages require explicit acknowledgment by the receiver.
- To ensure message delivery semantics, the receiver must call msg.Done() or msg.Error(err) to ensure delivery consistency.
3. Streams opened by the Multiplexer logically represent direct communication between microservices and edge nodes.
- Once the other side receives the stream, all functionalities on this stream will directly reach the other side, bypassing Frontier's routing policies.
## Usage
Detailed usage guide: [docs/USAGE.md](./docs/USAGE.md)
## Configuration
Detailed configuration guide: [docs/CONFIGURATION.md](./docs/CONFIGURATION.md)
## Deployment
In a single Frontier instance, you can choose the following methods to deploy your Frontier instance based on your environment.
### Docker
You can also run the RTMP example if you want to see Frontier's stream model used for traffic relay:
```bash
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
# Terminal 1
./bin/rtmp_service
# Terminal 2
./bin/rtmp_edge
```
### Docker-Compose
Demo video:
```bash
git clone https://github.com/singchia/frontier.git
cd dist/compose
docker-compose up -d frontier
```
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
### Helm
## Docs
If you are in a Kubernetes environment, you can use Helm to quickly deploy an instance.
README is intentionally optimized for fast understanding and fast conversion. The implementation details, configuration surface, deployment playbooks, and cluster operations live in the docs.
```bash
git clone https://github.com/singchia/frontier.git
cd dist/helm
helm install frontier ./ -f values.yaml
```
Start here:
Your microservice should connect to ```service/frontier-servicebound-svc:30011```, and your edge node can connect to the NodePort where `:30012` is located.
- [Usage Guide](./docs/USAGE.md)
- [Configuration Guide](./docs/CONFIGURATION.md)
- [Technical Docs Index](./docs/README.md)
- [Systemd Deployment](./dist/systemd/README.md)
- [Docker Compose Deployment](./dist/compose/README.md)
- [Helm and Operator Deployment](./dist/helm/README.md)
- [Roadmap](./ROADMAP.md)
### Systemd
If you are evaluating Frontier for production, the docs cover:
Use the dedicated Systemd docs:
[dist/systemd/README.md](./dist/systemd/README.md)
### Operator
See the cluster deployment section below.
## Cluster
### Frontier + Frontlas Architecture
<img src="./docs/diagram/frontlas.png" width="100%">
The additional Frontlas component is used to build the cluster. Frontlas is also a stateless component and does not store other information in memory, so it requires additional dependency on Redis. You need to provide a Redis connection information to Frontlas, supporting `redis`, `sentinel`, and `redis-cluster`.
- _Frontier_: Communication component between microservices and edge data planes.
- _Frontlas_: Named Frontier Atlas, a cluster management component that records metadata and active information of microservices and edges in Redis.
Frontier needs to proactively connect to Frontlas to report its own, microservice, and edge active and status. The default ports for Frontlas are:
- `:40011` for microservices connection, replacing the 30011 port in a single Frontier instance.
- `:40012` for Frontier connection to report status.
You can deploy any number of Frontier instances as needed, and for Frontlas, deploying two instances separately can ensure HA (High Availability) since it does not store state and has no consistency issues.
### Configuration
**Frontier**'s `frontier.yaml` needs to add the following configuration:
```yaml
frontlas:
enable: true
dial:
network: tcp
addr:
- 127.0.0.1:40012
metrics:
enable: false
interval: 0
daemon:
# Unique ID within the Frontier cluster
frontier_id: frontier01
```
Frontier needs to connect to Frontlas to report its own, microservice, and edge active and status.
**Frontlas**'s `frontlas.yaml` minimal configuration:
```yaml
control_plane:
listen:
# Microservices connect to this address to discover edges in the cluster
network: tcp
addr: 0.0.0.0:40011
frontier_plane:
# Frontier connects to this address
listen:
network: tcp
addr: 0.0.0.0:40012
expiration:
# Expiration time for microservice metadata in Redis
service_meta: 30
# Expiration time for edge metadata in Redis
edge_meta: 30
redis:
# Support for standalone, sentinel, and cluster connections
mode: standalone
standalone:
network: tcp
addr: redis:6379
db: 0
```
### Usage
Since Frontlas is used to discover available Frontiers, microservices need to adjust as follows:
**Microservice Getting Service**
```golang
package main
import (
"net"
"github.com/singchia/frontier/api/dataplane/v1/service"
)
func main() {
// Use NewClusterService to get Service
svc, err := service.NewClusterService("127.0.0.1:40011")
// Start using service, everything else remains unchanged
}
```
**Edge Node Getting Connection Address**
For edge nodes, they still connect to Frontier but can get available Frontier addresses from Frontlas. Frontlas provides an interface to list Frontier instances:
```bash
curl -X GET http://127.0.0.1:40011/cluster/v1/frontiers
```
You can wrap this interface to provide load balancing or high availability for edge nodes, or add mTLS to directly provide to edge nodes (not recommended).
Control Plane gRPC See [Protobuf Definition](./api/controlplane/frontlas/v1/cluster.proto).
The Frontlas control plane differs from Frontier as it is a cluster-oriented control plane, currently providing only read interfaces for the cluster.
```protobuf
service ClusterService {
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse);
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse);
}
```
## Kubernetes
### Operator
**Install CRD and Operator**
Follow these steps to install and deploy the Operator to your .kubeconfig environment:
```bash
git clone https://github.com/singchia/frontier.git
cd dist/crd
kubectl apply -f install.yaml
```
Check CRD:
```bash
kubectl get crd frontierclusters.frontier.singchia.io
```
Check Operator:
```bash
kubectl get all -n frontier-system
```
**FrontierCluster**
```yaml
apiVersion: frontier.singchia.io/v1alpha1
kind: FrontierCluster
metadata:
labels:
app.kubernetes.io/name: frontiercluster
app.kubernetes.io/managed-by: kustomize
name: frontiercluster
spec:
frontier:
# Single instance Frontier
replicas: 2
# Microservice side port
servicebound:
port: 30011
# Edge node side port
edgebound:
port: 30012
frontlas:
# Single instance Frontlas
replicas: 1
# Control plane port
controlplane:
port: 40011
redis:
# Dependent Redis configuration
addrs:
- rfs-redisfailover:26379
password: your-password
masterName: mymaster
redisType: sentinel
```
Save as`frontiercluster.yaml`and
```
kubectl apply -f frontiercluster.yaml
```
In 1 minute, you will have a 2-instance Frontier + 1-instance Frontlas cluster.
Check resource deployment status with:
```bash
kubectl get all -l app=frontiercluster-frontier
kubectl get all -l app=frontiercluster-frontlas
```
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
```
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
```
Your microservice should connect to `service/frontiercluster-frontlas-svc:40011`, and your edge node can connect to the NodePort where `:30012` is located.
## Development
### Roadmap
See [ROADMAP](./ROADMAP.md)
### Contributions
If you find any bugs, please open an issue, and project maintainers will respond promptly.
If you wish to submit features or more quickly address project issues, you are welcome to submit PRs under these simple conditions:
- Code style remains consistent
- Each submission includes one feature
- Submitted code includes unit tests
## Testing
### Stream Function
<img src="./docs/diagram/stream.png" width="100%">
- architecture and communication model
- RPC, messaging, and point-to-point stream semantics
- deployment on Docker, Compose, Helm, and Operator
- cluster mode with Frontlas and Redis
- development workflow and contribution guidelines
## Community
+150 -408
View File
@@ -13,38 +13,143 @@
</div>
Frontier是一个go开发的全双工开源长连接网关,旨在让微服务直达边缘节点或客户端,反之边缘节点或客户端也同样直达微服务。对于两者,提供了全双工的单双向RPC调用,消息发布和接收,以及点对点流的功能。Frontier符合云原生架构,可以使用Operator快速部署一个集群,具有高可用和弹性,轻松支撑百万边缘节点或客户端在线的需求。
> 面向长连接场景的 service-to-edge 双向通信网关
Frontier 是一个使用 Go 编写的开源网关,专门用于 **service <-> edge** 通信。它让后端服务和边缘节点可以在长连接上直接双向交互,内置 **双向 RPC**、**消息收发** 和 **点对点流**
它适用于两端都需要长期在线,并且需要主动互相调用、通知或建流的系统。Frontier **不是反向代理**,也 **不只是消息队列**。它更像是一层基础设施,让后端服务能够寻址并管理大规模在线边缘节点。
<p align="center">
<img src="./docs/diagram/frontier.png" width="100%" alt="Frontier 架构总览">
</p>
## Frontier 的独特之处
- **不是 API Gateway 那一类产品**Frontier 解决的是 backend-to-edge 通信,不只是南北向 HTTP 流量。
- **不是单纯 MQ**:它把双向 RPC、消息和流统一到同一套连接模型里。
- **不是普通隧道**:服务端可以直接寻址某个在线边缘节点,而不只是暴露一个端口。
- **天然面向在线节点集群**:适合设备、Agent、客户端、Connector 这种需要长期在线的场景。
## 你可以拿它做什么
<table>
<tr>
<td width="50%">
<img src="./docs/diagram/rtmp.png" alt="Frontier 流量中继示例">
<p><strong>流量中继和媒体传输</strong><br>通过点对点流承载 RTMP、中继代理、文件传输,或者任意自定义协议。</p>
</td>
<td width="50%">
<img src="./docs/diagram/stream.png" alt="Frontier 流模型">
<p><strong>远程 Agent 与设备集群</strong><br>让边缘节点保持在线,服务端可定向调用某个 edge,edge 也能反向调用后端服务。</p>
</td>
</tr>
</table>
## 一眼看懂
| 你的需求 | Frontier 提供的能力 |
| --- | --- |
| 后端要调用某个具体在线设备或 Agent | 基于长连接的 Service -> Edge RPC 与消息能力 |
| 边缘节点不能暴露入站端口,但要反向调用服务 | 同一套连接模型下的 Edge -> Service 调用 |
| 不只是 request/response,而是要传连续字节流 | Service 与 Edge 之间的点对点流 |
| 需要统一管理大规模在线节点 | 在线态、生命周期回调、控制面 API 与集群能力 |
## 目录
- [特性](#特性)
- [为什么是 Frontier](#为什么是-frontier)
- [Frontier 的独特之处](#frontier-的独特之处)
- [你可以拿它做什么](#你可以拿它做什么)
- [一眼看懂](#一眼看懂)
- [什么时候适合用 Frontier](#什么时候适合用-frontier)
- [真实场景](#真实场景)
- [对比](#对比)
- [快速开始](#快速开始)
- [架构](#架构)
- [使用](#使用)
- [配置](#配置)
- [部署](#部署)
- [集群](#集群)
- [Kubernetes](#kubernetes)
- [开发](#开发)
- [测试](#测试)
- [文档](#文档)
- [社区](#社区)
- [许可证](#许可证)
## 为什么是 Frontier
大多数基础设施更偏向下面几种通信模型:
- **service -> service**,例如 HTTP 或 gRPC
- **client -> service**,例如 API Gateway 或反向代理
- **事件广播/分发**,例如消息队列
Frontier 面向的是另一类问题:
- **service <-> edge** 之间的长连接、双向、状态化通信
- 后端服务需要主动调用某个在线的边缘节点
- 边缘节点需要在不暴露入站端口的情况下主动调用后端服务
- RPC 不够时,还需要在服务和边缘之间打开直连流
<p align="center">
<img src="./docs/diagram/frontlas.png" width="88%" alt="Frontier 集群与 Frontlas">
</p>
## 什么时候适合用 Frontier
如果你需要下面这些能力,Frontier 是合适的:
- 后端服务主动调用在线的设备、Agent、客户端或 Connector
- 边缘节点通过同一套连接模型主动调用后端服务
- 大规模长连接在线
- 用同一套数据面处理 RPC、消息和流
- 面向 service-to-edge 连接的集群部署和高可用
如果你只是下面这些需求,就不一定要用 Frontier:
- 只是做 service-to-service RPC,那么 gRPC 更简单
- 只是做 HTTP 入口、路由或代理,那么用 API Gateway 或 Envoy
- 只是做 pub/sub 或事件流,那么用 NATS 或 Kafka
- 只是做通用隧道,那么用 frp 或其他隧道工具
## 真实场景
- IoT 设备和终端集群
- 远程 Agent 和 Connector
- IM 和其他实时系统
- 游戏后端与在线客户端或边缘节点通信
- 基于 Connector 模式的零信任内网接入
- 通过点对点流做文件传输、媒体中继或流量代理
## 场景速览
| 场景 | 为什么适合 Frontier |
| --- | --- |
| 设备控制面 | 能直接寻址在线 edge,推指令、收状态,并维持长连接在线 |
| 远程 Connector 平台 | Connector 主动外连,不暴露入站端口,服务侧路由也更简单 |
| 实时业务系统 | 通知、RPC 和流都走同一条链路,适合长期在线连接 |
| 零信任内网接入 | 用 Agent 风格的 edge 做后端系统到私有资源的最后一跳桥接 |
## 对比
| 能力 | Frontier | gRPC | NATS | frp | Envoy |
| --- | --- | --- | --- | --- | --- |
| 以 service <-> edge 通信为核心模型 | 是 | 否 | 部分 | 否 | 否 |
| 后端可直接寻址某个在线边缘节点 | 是 | 否 | 部分 | 部分 | 否 |
| 边缘节点可主动调用后端服务 | 是 | 部分 | 是 | 否 | 否 |
| 支持 service 和 edge 之间的点对点流 | 是 | 部分 | 否 | 仅隧道 | 否 |
| 统一的 RPC + 消息 + 流模型 | 是 | 否 | 否 | 否 | 否 |
| 以大规模长连接在线为主要设计目标 | 是 | 否 | 部分 | 部分 | 否 |
这里的“部分”表示能力可以通过额外模式拼出来,但不是该系统的主通信模型。
## 快速开始
1. 启动单实例 Frontier
```bash
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2
```
2. 构建并运行示例:
2. 构建示例程序
```bash
make examples
```
运行 chatroom 示例:
3. 运行 chatroom 示例:
```bash
# 终端 1
@@ -54,405 +159,43 @@ make examples
./bin/chatroom_agent
```
演示视频:
chatroom 示例展示的是 Frontier 最基础的交互模型:长连接在线、边缘上下线事件,以及 service <-> edge 的消息交互。
如果你想看点对点流的能力,也可以运行 RTMP 示例:
```bash
# 终端 1
./bin/rtmp_service
# 终端 2
./bin/rtmp_edge
```
演示视频:
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
## 特性
## 文档
- **RPC** 微服务和边缘可以Call对方的函数(提前注册),并且在微服务侧支持负载均衡
- **消息** 微服务和边缘可以Publish对方的Topic,边缘可以Publish到外部MQ的Topic,微服务侧支持负载均衡
- **多路复用/流** 微服务可以直接在边缘节点打开一个流(连接),可以封装例如文件上传、代理等,天堑变通途
- **上线离线控制** 微服务可以注册边缘节点获取ID、上线离线回调,当这些事件发生,Frontier会调用这些函数
- **API简单** 在项目api目录下,分别对边缘和微服务提供了封装好的sdk,可以非常简单的基于这个sdk做开发
- **部署简单** 支持多种部署方式(docker docker-compose helm以及operator)来部署Frontier实例或集群
- **水平扩展** 提供了Frontiter和Frontlas集群,在单实例性能达到瓶颈下,可以水平扩展Frontier实例或集群
- **高可用** 支持集群部署,支持微服务和边缘节点永久重连sdk,在当前实例宕机情况时,切换新可用实例继续服务
- **支持控制面** 提供了gRPC和rest接口,允许运维人员对微服务和边缘节点查询或删除,删除即踢除目标下线
README 现在刻意只承担“快速看懂、快速转化”的职责。实现细节、配置面、部署方案和集群运维都放在文档里。
## 架构
建议从这里开始:
### 组件Frontier
- [使用指南](./docs/USAGE_zh.md)
- [配置指南](./docs/CONFIGURATION_zh.md)
- [技术文档索引](./docs/README.md)
- [Systemd 部署](./dist/systemd/README.md)
- [Docker Compose 部署](./dist/compose/README.md)
- [Helm 与 Operator 部署](./dist/helm/README.md)
- [路线图](./ROADMAP.md)
<img src="./docs/diagram/frontier.png" width="100%">
- _Service End_:微服务侧的功能入口,默认连接
- _Edge End_:边缘节点或客户端侧的功能入口
- _Publish/Receive_:发布和接收消息
- _Call/Register_:调用和注册函数
- _OpenStream/AcceptStream_:打开和接收点到点流(连接)
- _外部MQ_Frontier支持将从边缘节点Publish的消息根据配置的Topic转发到外部MQ
Frontier需要微服务和边缘节点两方都主动连接到Frontier,Service和Edge的元信息(接收TopicRPC,Service名等)可以在连接的时候携带过来。连接的默认端口是:
- ```:30011``` 提供给微服务连接,获取Service
- ```:30012``` 提供给边缘节点连接,获取Edge
- ```:30010``` 提供给运维人员或者程序使用的控制面
### 功能
<table><thead>
<tr>
<th>功能</th>
<th>发起方</th>
<th>接收方</th>
<th>方法</th>
<th>路由方式</th>
<th>描述</th>
</tr></thead>
<tbody>
<tr>
<td rowspan="2">Messager</td>
<td>Service</td>
<td>Edge</td>
<td>Publish</td>
<td>EdgeID+Topic</td>
<td>必须Publish到具体的EdgeID,默认Topic为空,Edge调用Receive接收,接收处理完成后必须调用msg.Done()或msg.Error(err)保障消息一致性</td>
</tr>
<tr>
<td>Edge</td>
<td>Service或外部MQ</td>
<td>Publish</td>
<td>Topic</td>
<td>必须Publish到Topic,由Frontier根据Topic选择某个Service或MQ</td>
</tr>
<tr>
<td rowspan="2">RPCer</td>
<td>Service</td>
<td>Edge</td>
<td>Call</td>
<td>EdgeID+Method</td>
<td>必须Call到具体的EdgeID,需要携带Method</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>Call</td>
<td>Method</td>
<td>必须Call到Method,由Frontier根据Method选择某个的Service</td>
</tr>
<tr>
<td rowspan="2">Multiplexer</td>
<td>Service</td>
<td>Edge</td>
<td>OpenStream</td>
<td>EdgeID</td>
<td>必须OpenStream到具体的EdgeID</td>
</tr>
<tr>
<td>Edge</td>
<td>Service</td>
<td>OpenStream</td>
<td>ServiceName</td>
<td>必须OpenStream到ServiceName,该ServiceName由Service初始化时携带的service.OptionServiceName指定</td>
</tr>
</tbody></table>
主要遵守以下设计原则:
1. 所有的消息、RPC和Stream都是点到点的传递
- 从微服务到边缘,一定要指定边缘节点ID
- 从边缘到微服务,Frontier根据Topic和Method路由,最终哈希选择一个微服务或外部MQ,默认根据```edgeid```哈希,你也可以选择```random```或```srcip```
2. 消息需要接收方明确结束
- 为了保障消息的传达语义,接收方一定需要msg.Done()或msg.Error(err),保障传达一致性
3. Multiplexer打开的流在逻辑上是微服务与边缘节点的直接通信
- 对方接收到流后,所有在这个流上功能都会直达对方,不会经过Frontierd的路由策略
## 使用
详细使用文档: [docs/USAGE_zh.md](./docs/USAGE_zh.md)
## 配置
详细配置文档: [docs/CONFIGURATION_zh.md](./docs/CONFIGURATION_zh.md)
## 部署
在单Frontier实例下,可以根据环境选择以下方式部署你的Frontier实例。
### docker
```
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.1.0
```
### docker-compose
```
git clone https://github.com/singchia/frontier.git
cd dist/compose
docker-compose up -d frontier
```
### helm
如果你是在k8s环境下,可以使用helm快速部署一个实例
```
git clone https://github.com/singchia/frontier.git
cd dist/helm
helm install frontier ./ -f values.yaml
```
你的微服务应该连接`service/frontier-servicebound-svc:30011`,你的边缘节点可以连接`:30012`所在的NodePort。
### Systemd
使用独立的 Systemd 文档:
[dist/systemd/README_cn.md](./dist/systemd/README_cn.md)
### operator
见下面集群部署章节
## 集群
### Frontier + Frontlas架构
<img src="./docs/diagram/frontlas.png" width="100%">
新增Frontlas组件用于构建集群,Frontlas同样也是无状态组件,并不在内存里留存其他信息,因此需要额外依赖Redis,你需要提供一个Redis连接信息给到Frontlas,支持 ```redis``` ```sentinel```和```redis-cluster```。
- _Frontier_:微服务和边缘数据面通信组件
- _Frontlas_:命名取自Frontier Atlas,集群管理组件,将微服务和边缘的元信息、活跃信息记录在Redis里
Frontier需要主动连接Frontlas以上报自己、微服务和边缘的活跃和状态,默认Frontlas的端口是:
- ```:40011``` 提供给微服务连接,代替微服务在单Frontier实例下连接的30011端口
- ```:40012``` 提供给Frontier连接,上报状态
你可以根据需要部署任意多个Frontier实例,而对于Frontlas,分开部署两个即可保障HA(高可用),因为不存储状态没有一致性问题。
### 配置
**Frontier**的frontier.yaml需要添加如下配置:
```yaml
frontlas:
enable: true
dial:
network: tcp
addr:
- 127.0.0.1:40012
tls:
metrics:
enable: false
interval: 0
daemon:
# Frontier集群内的唯一ID
frontier_id: frontier01
```
Frontier需要连接Frontlas,用来上报自己、微服务和边缘的活跃和状态。
**Frontlas**的frontlas.yaml最小化配置:
```yaml
control_plane:
listen:
# 微服务改连接这个地址,用来发现集群的边缘节点所在的Frontier
network: tcp
addr: 0.0.0.0:40011
frontier_plane:
# Frontier连接这个地址
listen:
network: tcp
addr: 0.0.0.0:40012
expiration:
# 微服务在redis内元信息的过期时间
service_meta: 30
# 边缘节点在redis内元信息的过期时间
edge_meta: 30
redis:
# 支持连接standalone、sentinel和cluster
mode: standalone
standalone:
network: tcp
addr: redis:6379
db: 0
```
更多详细配置见 [frontlas_all.yaml](./etc/frontlas_all.yaml)
### 使用
由于使用Frontlas来发现可用的Frontier,因此微服务需要做出调整如下:
**微服务获取Service**
```golang
package main
import (
"net"
"github.com/singchia/frontier/api/dataplane/v1/service"
)
func main() {
// 改使用NewClusterService来获取Service
svc, err := service.NewClusterService("127.0.0.1:40011")
// 开始使用service,其他一切保持不变
}
```
**边缘节点获取连接地址**
对于边缘节点来说,依然连接Frontier,不过可以从Frontlas来获取可用的Frontier地址,Frontlas提供了列举Frontier实例接口:
```
curl -X http://127.0.0.1:40011/cluster/v1/frontiers
```
你可以在这个接口上封装一下,提供给边缘节点做负载均衡或者高可用,或加上mTLS直接提供给边缘节点(不建议)。
**控制面GRPC** 详见[Protobuf定义](./api/controlplane/frontlas/v1/cluster.proto)
Frontlas控制面与Frontier不同,是面向集群的控制面,目前只提供了读取集群的接口
```protobuf
service ClusterService {
rpc GetFrontierByEdge(GetFrontierByEdgeIDRequest) returns (GetFrontierByEdgeIDResponse);
rpc ListFrontiers(ListFrontiersRequest) returns (ListFrontiersResponse);
rpc ListEdges(ListEdgesRequest) returns (ListEdgesResponse);
rpc GetEdgeByID(GetEdgeByIDRequest) returns (GetEdgeByIDResponse);
rpc GetEdgesCount(GetEdgesCountRequest) returns (GetEdgesCountResponse);
rpc ListServices(ListServicesRequest) returns (ListServicesResponse) ;
rpc GetServiceByID(GetServiceByIDRequest) returns (GetServiceByIDResponse) ;
rpc GetServicesCount(GetServicesCountRequest) returns (GetServicesCountResponse) ;
}
```
## Kubernetes
### Operator
**安装CRD和Operator**
按照以下步骤安装和部署Operator到你的.kubeconfig环境中:
```
git clone https://github.com/singchia/frontier.git
cd dist/crd
kubectl apply -f install.yaml
```
查看CRD
```
kubectl get crd frontierclusters.frontier.singchia.io
```
查看Operator
```
kubectl get all -n frontier-system
```
**FrontierCluster集群**
```yaml
apiVersion: frontier.singchia.io/v1alpha1
kind: FrontierCluster
metadata:
labels:
app.kubernetes.io/name: frontiercluster
app.kubernetes.io/managed-by: kustomize
name: frontiercluster
spec:
frontier:
# 单实例Frontier
replicas: 2
# 微服务侧端口
servicebound:
port: 30011
# 边缘节点侧端口
edgebound:
port: 30012
frontlas:
# 单实例Frontlas
replicas: 1
# 控制面端口
controlplane:
port: 40011
redis:
# 依赖的Redis配置
addrs:
- rfs-redisfailover:26379
password: your-password
masterName: mymaster
redisType: sentinel
```
保存为`frontiercluster.yaml`,执行
```
kubectl apply -f frontiercluster.yaml
```
1分钟,你即可拥有一个2实例Frontier+1实例Frontlas的集群。
通过一下来检查资源部署情况
> kubectl get all -l app=frontiercluster-frontier
> kubectl get all -l app=frontiercluster-frontlas
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontier-57d565c89-dn6n8 1/1 Running 0 7m22s
pod/frontiercluster-frontier-57d565c89-nmwmt 1/1 Running 0 7m22s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-edgebound-svc NodePort 10.233.23.174 <none> 30012:30012/TCP 8m7s
service/frontiercluster-servicebound-svc ClusterIP 10.233.29.156 <none> 30011/TCP 8m7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontier 2/2 2 2 7m22s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontier-57d565c89 2 2 2 7m22s
```
```
NAME READY STATUS RESTARTS AGE
pod/frontiercluster-frontlas-85c4fb6d9b-5clkh 1/1 Running 0 8m11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontiercluster-frontlas-svc ClusterIP 10.233.0.23 <none> 40011/TCP,40012/TCP 8m11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/frontiercluster-frontlas 1/1 1 1 8m11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1 8m11s
```
你的微服务应该连接`service/frontiercluster-frontlas-svc:40011`,你的边缘节点可以连接`:30012`所在的NodePort。
## 开发
### 路线图
详见 [ROADMAP](./ROADMAP.md)
### 贡献
如果你发现任何Bug,请提出Issue,项目Maintainers会及时响应相关问题。
如果你希望能够提交Feature,更快速解决项目问题,满足以下简单条件下欢迎提交PR:
* 代码风格保持一致
* 每次提交一个Feature
* 提交的代码都携带单元测试
## 测试
### 流功能测试
<img src="./docs/diagram/stream.png" width="100%">
如果你在评估 Frontier 是否适合生产环境,文档里已经覆盖:
- 架构与通信模型
- RPC、消息和点对点流的语义
- Docker、Compose、Helm、Operator 的部署方式
- 基于 Frontlas 和 Redis 的集群模式
- 开发流程与贡献规范
## 社区
@@ -460,12 +203,11 @@ replicaset.apps/frontiercluster-frontlas-85c4fb6d9b 1 1 1
<img src="./docs/diagram/wechat.JPG" width="30%">
</p>
添加以加入微信群组
欢迎加入微信交流群讨论和反馈。
## 许可证
Released under the [Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE)
基于 [Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE) 发布。
---
已经看到这里,点个Star⭐️吧♥️
如果这个项目对你有帮助,欢迎点一个 Star ⭐
+44 -13
View File
@@ -1,34 +1,66 @@
## Usage
Frontier is easiest to understand if you think about it as **service <-> edge connectivity**, not as a generic gateway.
Use this guide in the following order:
1. Understand the mental model
2. Run the example closest to your use case
3. Copy the SDK pattern you need on the service side or edge side
### Mental Model
- **Service side** connects to `:30011`
- **Edge side** connects to `:30012`
- **Service -> Edge** operations usually target a specific `edgeID`
- **Edge -> Service** operations route by declared method, topic, or service name
- **Streams** behave like direct `net.Conn` links between service and edge
If you only remember one thing, remember this:
> Frontier is for systems where backend services need to actively reach online edge nodes, and edge nodes also need to actively reach backend services.
### Examples
In the [examples/chatroom](../examples/chatroom) directory, there is a simple chatroom example implemented in just 100 lines of code. You can get the executable programs chatroom\_service and chatroom\_agent by running:
Start with the example that matches the job you want Frontier to do.
#### Chatroom: messaging and presence
In [examples/chatroom](../examples/chatroom), there is a simple chatroom example implemented in about 100 lines of code. It is the fastest way to understand:
- service <-> edge messaging
- edge online/offline notifications
- the basic long-lived connection model
Build the example binaries:
```
make examples
```
Run the example:
Run the demo:
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
In this example, you can see features like online/offline notifications and message publishing.
#### RTMP: point-to-point streams
**Live Streaming**
In [examples/rtmp](../examples/rtmp), there is a simple live streaming example implemented in about 80 lines of code. It is the fastest way to understand:
In the [examples/rtmp](../examples/rtmp) directory, there is a simple live streaming example implemented in just 80 lines of code. You can get the executable programs `rtmp_service` and `rtmp_edge` by running:
```
make examples
```
- service -> edge stream opening
- using Frontier as a stream transport rather than only RPC
- traffic relay for protocols such as RTMP
After running, use [OBS](https://obsproject.com/) to connect to `rtmp_edge` for live streaming proxy:
<img src="./diagram/rtmp.png" width="100%">
In this example, you can see Multiplexer and Stream functionality.
#### Which example should you start with?
### Using Frontier in Microservices
- If you care about commands, notifications, or device/agent messaging, start with **chatroom**
- If you care about file transfer, media relay, or custom protocol tunneling, start with **rtmp**
- If you want production integration patterns, continue with the SDK snippets below
### Service-Side SDK Patterns
**Getting Service on the Microservice Side**:
@@ -313,7 +345,7 @@ func echo(ctx context.Context, req geminio.Request, rsp geminio.Response) {
}
```
### Using Frontier on Edge Nodes
### Edge-Side SDK Patterns
**Getting Edge on the Edge Node Side**:
@@ -575,4 +607,3 @@ curl -X GET http://127.0.0.1:30010/v1/services/rpcs?service_id={service_id}
```
Note: gRPC/REST depends on the DAO backend, with two options: ```buntdb``` and ```sqlite3```. Both use in-memory mode. For performance considerations, the default backend uses buntdb, and the count field in the list interface always returns -1. When you configure the backend to ```sqlite3```, it means you have a strong OLTP requirement for connected microservices and edge nodes on Frontier, such as encapsulating the web on Frontier. In this case, the count will return the total number.
+44 -16
View File
@@ -1,36 +1,66 @@
## 使用
理解 Frontier 最快的方式,不是把它当成一个通用网关,而是把它看成 **service <-> edge 的双向连接层**
建议按下面顺序阅读:
1. 先建立心智模型
2. 先跑最贴近你场景的示例
3. 再复制需要的 Service 侧或 Edge 侧 SDK 用法
### 心智模型
- **Service 侧**连接 `:30011`
- **Edge 侧**连接 `:30012`
- **Service -> Edge** 通常需要指定具体 `edgeID`
- **Edge -> Service** 通常按 Method、Topic 或 ServiceName 路由
- **Stream** 在逻辑上就是 service 和 edge 之间的直连 `net.Conn`
如果只记住一句话,那就是:
> Frontier 适合“后端服务需要主动触达在线边缘节点,同时边缘节点也需要主动触达后端服务”的系统。
### 示例
**聊天室**
先从最贴近你目标的示例开始。
目录[examples/chatroom](../examples/chatroom)下有简单的聊天室示例,仅100行代码实现一个的聊天室功能,可以通过
#### 聊天室:消息和在线态
目录 [examples/chatroom](../examples/chatroom) 下有一个简单聊天室示例,大约 100 行代码,最适合用来理解:
- service <-> edge 的消息交互
- 边缘节点上线/离线通知
- 长连接下的基本交互模型
构建示例程序:
```
make examples
```
bin目录下得到```chatroom_service```和```chatroom_egent```可执行程序,运行示例:
`bin` 目录下得到 `chatroom_service``chatroom_agent` 可执行程序,运行示例:
https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30
在这个示例你可以看到上线离线通知,消息Publish等功能。
#### 直播代理:点对点流
**直播**
目录 [examples/rtmp](../examples/rtmp) 下有一个简单直播代理示例,大约 80 行代码,最适合用来理解:
目录[examples/rtmp](../examples/rtmp)下有简单的直播示例,仅80行代码实现一个的直播代理功能,可以通过
- service 到 edge 的点对点建流
- Frontier 不只是做 RPC,也可以做流承载
- RTMP 这类协议的中继或代理方式
```
make examples
```
在bin目录下得到```rtmp_service```和```rtmp_edge```可执行程序,运行后,使用[OBS](https://obsproject.com/)连接rtmp_edge即可直播代理:
运行后,使用 [OBS](https://obsproject.com/) 连接 `rtmp_edge` 即可做直播代理:
<img src="./diagram/rtmp.png" width="100%">
在这个示例你可以看到Multiplexer和Stream功能。
#### 应该先跑哪个示例?
### 微服务如何使用
- 如果你关心命令下发、通知、设备/Agent 消息交互,先看 **chatroom**
- 如果你关心文件传输、媒体中继或自定义协议代理,先看 **rtmp**
- 如果你已经明确要接 SDK,继续看下面的代码片段
### Service 侧常见模式
**微服务侧获取Service**
@@ -318,7 +348,7 @@ func echo(ctx context.Context, req geminio.Request, rsp geminio.Response) {
}
```
### 边缘节点/客户端如何使用
### Edge 侧常见模式
**边缘节点侧获取Edge**
@@ -582,5 +612,3 @@ curl -X GET http://127.0.0.1:30010/v1/services/rpcs?service_id={service_id}
```
**注意**gRPC/Rest依赖dao backend,有两个选项```buntdb```和```sqlite```,都是使用的in-memory模式,为性能考虑,默认backend使用buntdb,并且列表接口返回字段count永远是-1,当你配置backend为sqlite3时,会认为你对在Frontier上连接的微服务和边缘节点有强烈的OLTP需求,例如在Frontier上封装web,此时count才会返回总数。
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+5
View File
@@ -0,0 +1,5 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+48
View File
@@ -0,0 +1,48 @@
# Frontier Website
This directory contains the marketing site and docs site for Frontier, built with Next.js 16.
## Local Development
Install dependencies and start the development server:
```bash
npm install
npm run dev
```
Open `http://localhost:3000`.
Useful commands:
```bash
npm run lint
npm run build
```
## Deploying to Vercel
This app is intended to be deployed from the `website/` directory as its project root.
Recommended Vercel project settings:
- Framework Preset: `Next.js`
- Root Directory: `website`
- Install Command: `npm install`
- Build Command: `npm run build`
- Output Directory: leave empty
- Node version: use the project default from Vercel unless you need to pin it
Once linked, you can deploy with:
```bash
npx vercel
npx vercel --prod
```
## What This Site Covers
- Homepage positioning for Frontier
- Why Frontier / use cases
- Examples and docs pages
- Architecture and deployment docs
+18
View File
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;
+10
View File
@@ -0,0 +1,10 @@
import path from 'path';
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
turbopack: {
root: path.join(__dirname, '..'),
},
};
export default nextConfig;
+7466
View File
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
{
"name": "website",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --webpack",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"framer-motion": "^12.38.0",
"lucide-react": "^1.7.0",
"next": "16.2.2",
"playwright": "^1.59.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@tailwindcss/typography": "^0.5.19",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.2",
"tailwindcss": "^4",
"typescript": "^5"
}
}
+7
View File
@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

+40
View File
@@ -0,0 +1,40 @@
import { chromium } from 'playwright';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pages = [
{ name: 'home', url: 'http://localhost:3000' },
{ name: 'why-frontier', url: 'http://localhost:3000/why-frontier' },
{ name: 'architecture', url: 'http://localhost:3000/architecture' },
{ name: 'use-cases', url: 'http://localhost:3000/use-cases' },
{ name: 'examples', url: 'http://localhost:3000/examples' },
{ name: 'docs', url: 'http://localhost:3000/docs' },
{ name: 'docs-usage', url: 'http://localhost:3000/docs/usage' },
{ name: 'docs-configuration', url: 'http://localhost:3000/docs/configuration' },
{ name: 'docs-cluster', url: 'http://localhost:3000/docs/cluster' },
{ name: 'docs-operator', url: 'http://localhost:3000/docs/operator' },
];
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
});
for (const page of pages) {
const p = await context.newPage();
await p.goto(page.url, { waitUntil: 'networkidle' });
await p.waitForTimeout(500);
await p.screenshot({
path: path.join(__dirname, `screenshots/${page.name}.png`),
fullPage: true,
});
console.log(`${page.name}`);
await p.close();
}
await browser.close();
console.log('Done.');
})();
+53
View File
@@ -0,0 +1,53 @@
export default function Architecture() {
return (
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<div className="max-w-3xl mb-16">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">Architecture</h1>
<p className="text-xl text-slate-400">
A deep dive into how Frontier routes traffic, manages state, and scales.
</p>
</div>
<div className="bg-white/5 border border-white/10 rounded-2xl p-8 mb-16 flex flex-col items-center justify-center min-h-[400px]">
{/* Placeholder for the architecture diagram */}
<img src="/docs/diagram/frontier.png" alt="Frontier Architecture" className="w-full max-w-4xl rounded-lg" />
</div>
<div className="grid md:grid-cols-2 gap-12">
<div>
<h2 className="text-2xl font-bold text-white mb-6">Connection Model</h2>
<p className="text-slate-400 mb-4">
Unlike traditional gateways where clients connect to the gateway and the gateway connects to upstream services, in Frontier, <strong>both microservices and edge nodes actively connect to Frontier</strong>.
</p>
<ul className="space-y-3 text-slate-400 list-disc list-inside">
<li><strong className="text-white">Port 30011:</strong> For microservices to connect.</li>
<li><strong className="text-white">Port 30012:</strong> For edge nodes to connect.</li>
<li><strong className="text-white">Port 30010:</strong> Control plane APIs for operators.</li>
</ul>
</div>
<div>
<h2 className="text-2xl font-bold text-white mb-6">Routing Model</h2>
<p className="text-slate-400 mb-4">
All Messages, RPCs, and Streams are point-to-point transmissions:
</p>
<ul className="space-y-3 text-slate-400 list-disc list-inside">
<li><strong>Service to Edge:</strong> Must specify the Edge ID.</li>
<li><strong>Edge to Service:</strong> Frontier routes based on Topic and Method, selecting a service via hashing (default by edgeid, or random/srcip).</li>
<li><strong>Multiplexer Streams:</strong> Bypass routing completely for direct byte-level proxying.</li>
</ul>
</div>
</div>
<div className="mt-16 pt-16 border-t border-white/10">
<h2 className="text-2xl font-bold text-white mb-6">Consistency & Reliability</h2>
<div className="bg-blue-500/10 border border-blue-500/20 p-6 rounded-xl">
<h3 className="text-lg font-bold text-blue-400 mb-2">Explicit Acknowledgment</h3>
<p className="text-slate-300">
To ensure message delivery semantics, Frontier requires the receiving end to explicitly call <code>msg.Done()</code> or <code>msg.Error(err)</code>. This guarantees consistency across distributed edge environments.
</p>
</div>
</div>
</div>
);
}
+89
View File
@@ -0,0 +1,89 @@
export default function ClusterArchitecture() {
return (
<div className="pb-16 max-w-4xl mx-auto">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-4">Cluster Architecture</h1>
<p className="text-xl text-zinc-400">
Scale Frontier horizontally to handle millions of connections using Frontlas.
</p>
</div>
<div className="prose prose-invert prose-blue max-w-none prose-img:rounded-xl prose-img:border prose-img:border-zinc-800">
<p>
While a standalone Frontier instance is powerful, production environments requiring High Availability (HA) and horizontal scaling should deploy the <strong>Frontier + Frontlas</strong> cluster architecture.
</p>
<img src="/docs/diagram/frontlas.png" alt="Frontlas Cluster Architecture" />
<h2>What is Frontlas?</h2>
<p>
<strong>Frontlas</strong> (Frontier Atlas) is a stateless cluster management component. It acts as the registry and control plane for multiple Frontier gateway instances.
</p>
<ul>
<li><strong>Stateless:</strong> Frontlas does not store state in memory. It uses Redis to maintain metadata and active connection states.</li>
<li><strong>Port 40011:</strong> Microservices connect here to discover available Edge nodes across the entire cluster.</li>
<li><strong>Port 40012:</strong> Frontier instances connect here to report their status and the status of edges connected to them.</li>
</ul>
<h2>How it works</h2>
<ol>
<li>You can deploy any number of Frontier instances.</li>
<li>Each Frontier instance connects to Frontlas to report heartbeat and metadata.</li>
<li>Edge nodes connect to any available Frontier instance (usually balanced by a load balancer or NodePort).</li>
<li>Microservices query Frontlas to find out which Frontier instance currently holds the connection to the target Edge node, and then establishes communication.</li>
</ol>
<h2>Cluster Configuration</h2>
<h3>1. Frontier Configuration</h3>
<p>In your <code>frontier.yaml</code>, enable Frontlas and set the instance ID:</p>
<pre><code className="language-yaml">{`frontlas:
enable: true
dial:
network: tcp
addr:
- 127.0.0.1:40012
daemon:
# Must be unique within the cluster
frontier_id: frontier01`}</code></pre>
<h3>2. Frontlas Configuration</h3>
<p>Configure <code>frontlas.yaml</code> to point to your Redis instance:</p>
<pre><code className="language-yaml">{`control_plane:
listen:
network: tcp
addr: 0.0.0.0:40011
frontier_plane:
listen:
network: tcp
addr: 0.0.0.0:40012
redis:
mode: standalone # Supports standalone, sentinel, and cluster
standalone:
network: tcp
addr: redis:6379
db: 0`}</code></pre>
<h2>Updating Microservices for Cluster Mode</h2>
<p>When running in cluster mode, microservices must initialize using <code>NewClusterService</code> pointing to Frontlas, rather than pointing to a single Frontier instance.</p>
<pre><code className="language-go">{`package main
import (
"github.com/singchia/frontier/api/dataplane/v1/service"
)
func main() {
// Point to Frontlas (Port 40011) instead of Frontier (Port 30011)
svc, err := service.NewClusterService("127.0.0.1:40011")
// The rest of the usage remains exactly the same!
// svc.Publish(...)
// svc.Call(...)
}`}</code></pre>
</div>
</div>
);
}
@@ -0,0 +1,92 @@
export default function ConfigurationGuide() {
return (
<div className="pb-16 max-w-4xl mx-auto">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-4">Configuration</h1>
<p className="text-xl text-zinc-400">
Customize your Frontier gateway with <code>frontier.yaml</code>.
</p>
</div>
<div className="prose prose-invert prose-blue max-w-none prose-pre:bg-[#0D0D0D] prose-pre:border prose-pre:border-zinc-800">
<p>
Save your configuration as <code>frontier.yaml</code> and mount it to the container at <code>/usr/conf/frontier.yaml</code> to take effect.
</p>
<h2>Minimal Configuration</h2>
<p>To get started, you only need to configure the listening addresses for microservices and edge nodes:</p>
<pre><code className="language-yaml">{`# Microservice configuration
servicebound:
listen:
network: tcp
addr: 0.0.0.0:30011
# Edge node configuration
edgebound:
listen:
network: tcp
addr: 0.0.0.0:30012
# Allow Frontier to allocate edgeID if no ID service is registered
edgeid_alloc_when_no_idservice_on: true`}</code></pre>
<h2>TLS and mTLS</h2>
<p>Frontier supports TLS for both microservices and edge nodes. It also supports mTLS where Frontier verifies the client certificate.</p>
<pre><code className="language-yaml">{`edgebound:
listen:
addr: 0.0.0.0:30012
network: tcp
tls:
enable: true
certs:
- cert: edgebound.cert
key: edgebound.key
insecure_skip_verify: false
# Enable mTLS: verify client certificates against a CA
mtls: true
ca_certs:
- ca1.cert`}</code></pre>
<h2>External Message Queue (MQ) Routing</h2>
<p>Frontier can automatically forward messages published by Edge nodes directly to external Message Queues based on topics.</p>
<h3>Kafka</h3>
<p>If an Edge node publishes to a topic listed in this array, Frontier will forward it to Kafka. If microservices also declare the same topic, Frontier hashes the traffic between them.</p>
<pre><code className="language-yaml">{`mqm:
kafka:
enable: true
addrs:
- "kafka:9092"
producer:
topics:
- "sensor/temperature"
- "device/events"`}</code></pre>
<h3>Redis</h3>
<pre><code className="language-yaml">{`mqm:
redis:
enable: true
addrs:
- "redis:6379"
db: 0
password: "your-password"
producer:
channels:
- "alerts"
- "metrics"`}</code></pre>
<p>Frontier also supports <strong>AMQP (RabbitMQ)</strong>, <strong>NATS</strong> (with Jetstream support), and <strong>NSQ</strong>.</p>
<h2>Routing Strategy & Database</h2>
<pre><code className="language-yaml">{`dao:
# Supports buntdb and sqlite3. Both use in-memory mode to keep Frontier stateless.
# buntdb is recommended for maximum performance.
backend: buntdb
exchange:
# How Frontier hashes and routes traffic when multiple microservices subscribe
# to the same topic/method. Options: edgeid, srcip, random
hashby: edgeid`}</code></pre>
</div>
</div>
);
}
+21
View File
@@ -0,0 +1,21 @@
import { DocsSidebar } from '@/components/DocsSidebar';
export default function DocsLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="w-full max-w-7xl mx-auto px-6 lg:px-8 py-12 flex flex-col md:flex-row gap-12">
{/* Sidebar for Desktop */}
<aside className="hidden md:block w-64 shrink-0 border-r border-zinc-800/50 min-h-[calc(100vh-120px)]">
<DocsSidebar />
</aside>
{/* Main Content Area */}
<main className="flex-1 min-w-0 prose prose-invert prose-blue max-w-4xl">
{children}
</main>
</div>
);
}
+69
View File
@@ -0,0 +1,69 @@
export default function OperatorGuide() {
return (
<div className="pb-16 max-w-4xl mx-auto">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-4">Kubernetes Operator</h1>
<p className="text-xl text-zinc-400">
The official Kubernetes Operator for deploying Frontier clusters.
</p>
</div>
<div className="prose prose-invert prose-blue max-w-none prose-pre:bg-[#0D0D0D] prose-pre:border prose-pre:border-zinc-800">
<p>
The Frontier Operator automates the deployment, provisioning, and scaling of Frontier and Frontlas clusters inside Kubernetes.
</p>
<h2>1. Install the CRD and Operator</h2>
<p>First, clone the repository and apply the Custom Resource Definitions (CRDs):</p>
<pre><code className="language-bash">{`git clone https://github.com/singchia/frontier.git
cd frontier/dist/crd
kubectl apply -f install.yaml`}</code></pre>
<p>Verify that the CRD is installed:</p>
<pre><code className="language-bash">{`kubectl get crd frontierclusters.frontier.singchia.io`}</code></pre>
<p>Verify that the Operator is running:</p>
<pre><code className="language-bash">{`kubectl get all -n frontier-system`}</code></pre>
<h2>2. Deploy a FrontierCluster</h2>
<p>Create a file named <code>frontiercluster.yaml</code>. This example deploys 2 Frontier gateways and 1 Frontlas control plane relying on an existing Redis sentinel.</p>
<pre><code className="language-yaml">{`apiVersion: frontier.singchia.io/v1alpha1
kind: FrontierCluster
metadata:
name: my-frontier-cluster
spec:
frontier:
replicas: 2
servicebound:
port: 30011
edgebound:
port: 30012
frontlas:
replicas: 1
controlplane:
port: 40011
redis:
addrs:
- rfs-redisfailover:26379
password: your-password
masterName: mymaster
redisType: sentinel`}</code></pre>
<p>Apply the cluster configuration:</p>
<pre><code className="language-bash">{`kubectl apply -f frontiercluster.yaml`}</code></pre>
<h2>3. Verify the Cluster</h2>
<p>Within a minute, your HA cluster will be ready. You can check the status of the pods:</p>
<pre><code className="language-bash">{`kubectl get all -l app=frontiercluster-frontier
kubectl get all -l app=frontiercluster-frontlas`}</code></pre>
<h2>4. Connect your workloads</h2>
<ul>
<li><strong>Microservices:</strong> Should connect to <code>service/frontiercluster-frontlas-svc:40011</code></li>
<li><strong>Edge Nodes:</strong> Can connect to the NodePort where <code>:30012</code> is exposed externally.</li>
</ul>
</div>
</div>
);
}
+106
View File
@@ -0,0 +1,106 @@
import Link from 'next/link';
export default function DocsOverview() {
return (
<div className="pb-16">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-4">Frontier Documentation</h1>
<p className="text-xl text-zinc-400">
Learn the service-to-edge model, run the fastest demos, and then integrate the SDKs.
</p>
</div>
<p className="text-zinc-300 leading-relaxed mb-10">
Frontier is easiest to understand in this order: first the communication model, then the examples, then the SDK snippets. If your backend services need to directly reach online edge nodes and edge nodes also need to reach services back, you are in the right place.
</p>
<div className="grid md:grid-cols-3 gap-4 mb-14">
<Link href="/examples" className="rounded-2xl border border-zinc-800 bg-[#111] p-6 hover:border-zinc-700 transition-colors">
<div className="text-sm text-blue-400 mb-2">Start here</div>
<h2 className="text-xl font-bold text-white mb-2">Examples</h2>
<p className="text-sm text-zinc-400">Use chatroom for messaging and presence, or RTMP for stream transport.</p>
</Link>
<Link href="/why-frontier" className="rounded-2xl border border-zinc-800 bg-[#111] p-6 hover:border-zinc-700 transition-colors">
<div className="text-sm text-emerald-400 mb-2">Positioning</div>
<h2 className="text-xl font-bold text-white mb-2">Why Frontier</h2>
<p className="text-sm text-zinc-400">See when Frontier is the right model, and when another tool is simpler.</p>
</Link>
<Link href="/docs/usage" className="rounded-2xl border border-zinc-800 bg-[#111] p-6 hover:border-zinc-700 transition-colors">
<div className="text-sm text-cyan-400 mb-2">Integrate</div>
<h2 className="text-xl font-bold text-white mb-2">Usage Guide</h2>
<p className="text-sm text-zinc-400">Copy the service-side and edge-side SDK patterns you actually need.</p>
</Link>
</div>
<h2 className="text-2xl font-bold text-white mt-12 mb-6">Quick Start Guide</h2>
<div className="space-y-12">
<div className="bg-[#111] border border-zinc-800 rounded-2xl p-8 relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-blue-500"></div>
<h3 className="text-lg font-bold text-white mb-4 flex items-center gap-2">
<span className="flex items-center justify-center w-6 h-6 rounded-full bg-blue-500/20 text-blue-400 text-xs">1</span>
Start the Gateway
</h3>
<p className="text-zinc-400 mb-4 text-sm">Run a standalone Frontier instance using Docker. This exposes the Service port (30011) and the Edge port (30012).</p>
<div className="bg-[#050505] border border-zinc-800 rounded-xl p-4 font-mono text-sm text-emerald-400 overflow-x-auto">
docker run -d --name frontier -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2
</div>
</div>
<div className="bg-[#111] border border-zinc-800 rounded-2xl p-8 relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-purple-500"></div>
<h3 className="text-lg font-bold text-white mb-4 flex items-center gap-2">
<span className="flex items-center justify-center w-6 h-6 rounded-full bg-purple-500/20 text-purple-400 text-xs">2</span>
Microservice Integration
</h3>
<p className="text-zinc-400 mb-4 text-sm">Microservices connect to port 30011. Start with a dialer, then create the service client.</p>
<div className="bg-[#050505] border border-zinc-800 rounded-xl p-4 font-mono text-sm overflow-x-auto leading-relaxed">
<div className="text-zinc-400 mb-2">import (</div>
<div className="pl-4 text-zinc-400">&quot;net&quot;</div>
<div className="pl-4 text-zinc-400">&quot;github.com/singchia/frontier/api/dataplane/v1/service&quot;</div>
<div className="text-zinc-400 mb-2">)</div>
<div><span className="text-blue-400">func</span> <span className="text-emerald-400">main</span>() &#123;</div>
<div className="pl-4 text-zinc-500">dialer := func() (net.Conn, error) &#123;</div>
<div className="pl-8 text-zinc-300">return net.Dial(<span className="text-amber-300">&quot;tcp&quot;</span>, <span className="text-amber-300">&quot;127.0.0.1:30011&quot;</span>)</div>
<div className="pl-4 text-zinc-500">&#125;</div>
<div className="pl-4 text-zinc-300">svc, err := service.<span className="text-cyan-300">NewService</span>(dialer)</div>
<div className="pl-4 text-zinc-300">if err != nil &#123; panic(err) &#125;</div>
<br/>
<div className="pl-4 text-zinc-500">{'// Now the service can receive messages, register RPC, or open streams'}</div>
<div className="text-zinc-300">&#125;</div>
</div>
</div>
<div className="bg-[#111] border border-zinc-800 rounded-2xl p-8 relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-cyan-500"></div>
<h3 className="text-lg font-bold text-white mb-4 flex items-center gap-2">
<span className="flex items-center justify-center w-6 h-6 rounded-full bg-cyan-500/20 text-cyan-400 text-xs">3</span>
Edge Node Integration
</h3>
<p className="text-zinc-400 mb-4 text-sm">Edge nodes (IoT, agents, clients) connect to port 30012. Then they can publish, register RPC, call services, or accept streams.</p>
<div className="bg-[#050505] border border-zinc-800 rounded-xl p-4 font-mono text-sm overflow-x-auto leading-relaxed">
<div className="text-zinc-400 mb-2">import (</div>
<div className="pl-4 text-zinc-400">&quot;net&quot;</div>
<div className="pl-4 text-zinc-400">&quot;github.com/singchia/frontier/api/dataplane/v1/edge&quot;</div>
<div className="text-zinc-400 mb-2">)</div>
<div><span className="text-blue-400">func</span> <span className="text-emerald-400">main</span>() &#123;</div>
<div className="pl-4 text-zinc-500">dialer := func() (net.Conn, error) &#123;</div>
<div className="pl-8 text-zinc-300">return net.Dial(<span className="text-amber-300">&quot;tcp&quot;</span>, <span className="text-amber-300">&quot;127.0.0.1:30012&quot;</span>)</div>
<div className="pl-4 text-zinc-500">&#125;</div>
<div className="pl-4 text-zinc-300">eg, err := edge.<span className="text-cyan-300">NewEdge</span>(dialer)</div>
<br/>
<div className="pl-4 text-zinc-500">{'// Now the edge can publish, register methods, call services, or open streams'}</div>
<div className="text-zinc-300">&#125;</div>
</div>
</div>
</div>
<div className="mt-16 flex items-center justify-between pt-8 border-t border-zinc-800">
<div></div>
<Link href="/docs/usage" className="inline-flex items-center text-blue-400 hover:text-blue-300 font-medium">
Next: Usage Guide &rarr;
</Link>
</div>
</div>
);
}
+172
View File
@@ -0,0 +1,172 @@
import Link from 'next/link';
export default function UsageGuide() {
return (
<div className="pb-16 max-w-4xl mx-auto">
<div className="mb-12">
<h1 className="text-4xl font-bold text-white mb-4">Usage Guide</h1>
<p className="text-xl text-zinc-400">
Learn the fastest mental model first, then copy the service-side and edge-side patterns you need.
</p>
</div>
<div className="grid md:grid-cols-3 gap-4 mb-12">
<Link href="/examples" className="rounded-2xl border border-zinc-800 bg-[#111] p-5 hover:border-zinc-700 transition-colors">
<div className="text-sm text-blue-400 mb-2">Fastest path</div>
<h2 className="text-lg font-bold text-white mb-2">Run examples</h2>
<p className="text-sm text-zinc-400">Use chatroom for messaging and presence, or RTMP for streams.</p>
</Link>
<div className="rounded-2xl border border-zinc-800 bg-[#111] p-5">
<div className="text-sm text-emerald-400 mb-2">Mental model</div>
<p className="text-sm text-zinc-300 leading-relaxed">Services connect to <code>:30011</code>. Edges connect to <code>:30012</code>. Service -&gt; edge usually targets a specific <code>edgeID</code>.</p>
</div>
<div className="rounded-2xl border border-zinc-800 bg-[#111] p-5">
<div className="text-sm text-cyan-400 mb-2">Why it feels different</div>
<p className="text-sm text-zinc-300 leading-relaxed">Frontier uses one service-to-edge model for RPC, messaging, and streams instead of treating them as separate systems.</p>
</div>
</div>
<div className="prose prose-invert prose-blue max-w-none prose-pre:bg-[#0D0D0D] prose-pre:border prose-pre:border-zinc-800">
<h2>Start with the right example</h2>
<ul>
<li>Choose <strong>chatroom</strong> if you want to understand presence, messaging, and the basic long-lived connection model.</li>
<li>Choose <strong>rtmp</strong> if you care about proxying traffic, file transfer, media relay, or other point-to-point stream use cases.</li>
<li>Return to this page once you know which SDK pattern you need to copy into production code.</li>
</ul>
<h2>Using Frontier in Microservices</h2>
<h3>1. Getting the Service Client</h3>
<p>To connect your microservice to Frontier, use the <code>NewService</code> method with a dialer connecting to the Service port (default: 30011).</p>
<pre><code className="language-go">{`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
}`}</code></pre>
<h3>2. Handling Edge Presence (Online/Offline)</h3>
<p>You can register callbacks to know when Edge nodes connect or disconnect from the gateway.</p>
<pre><code className="language-go">{`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
}`}</code></pre>
<h3>3. Service to Edge RPC (Command & Control)</h3>
<p>A Service can execute a method directly on an Edge node by its <code>edgeID</code>.</p>
<pre><code className="language-go">{`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)
}
}`}</code></pre>
<hr className="my-12 border-zinc-800" />
<h2>Using Frontier on Edge Nodes</h2>
<h3>1. Getting the Edge Client</h3>
<p>Edge nodes (IoT devices, agents, clients) connect to the Edge port (default: 30012). You must provide a unique Edge ID if the Service isn&apos;t allocating them.</p>
<pre><code className="language-go">{`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)
}
}`}</code></pre>
<h3>2. Edge Registers RPC for Service to Call</h3>
<p>For the Service to be able to issue commands to the Edge, the Edge must register methods.</p>
<pre><code className="language-go">{`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..."))
}`}</code></pre>
<h3>3. Edge Publishes Telemetry (Messaging)</h3>
<p>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.</p>
<pre><code className="language-go">{`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)
}`}</code></pre>
<hr className="my-12 border-zinc-800" />
<h2>Advanced: Point-to-Point Streams</h2>
<p>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.</p>
<h3>Service Opening a Stream to an Edge</h3>
<pre><code className="language-go">{`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)
}`}</code></pre>
</div>
</div>
);
}
+150
View File
@@ -0,0 +1,150 @@
import { Terminal, Code2, Radio, ArrowRight } from 'lucide-react';
export default function Examples() {
return (
<div className="w-full max-w-7xl mx-auto px-6 lg:px-8 py-24">
<div className="max-w-3xl mb-16">
<h1 className="text-4xl md:text-6xl font-extrabold tracking-tighter text-white mb-6">
Examples & Demos
</h1>
<p className="text-xl text-zinc-400 font-light leading-relaxed">
The fastest way to decide whether Frontier fits your system is to run the example closest to your use case.
</p>
</div>
<div className="grid lg:grid-cols-2 gap-8">
{/* Chatroom Demo */}
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden flex flex-col group">
<div className="bg-zinc-900/50 p-8 border-b border-zinc-800">
<div className="flex items-center justify-between mb-4">
<div className="w-12 h-12 rounded-2xl bg-blue-500/10 flex items-center justify-center border border-blue-500/20">
<Terminal className="w-6 h-6 text-blue-400" />
</div>
<span className="px-3 py-1 rounded-full bg-zinc-800 text-xs font-medium text-zinc-300 border border-zinc-700">Go</span>
</div>
<h2 className="text-2xl font-bold text-white mb-2">
Chatroom Demo
</h2>
<p className="text-zinc-400 text-sm">Start here if you care about command flow, notifications, presence, and service &lt;-&gt; edge messaging.</p>
</div>
<div className="p-8 flex-1 flex flex-col">
<div className="bg-[#111] border border-zinc-800/50 rounded-xl p-5 font-mono text-sm text-zinc-300 mb-8 overflow-x-auto">
<div className="text-zinc-500 mb-2"># 1. Start Frontier Gateway</div>
<div className="mb-6 text-emerald-400">docker run -d -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2</div>
<div className="text-zinc-500 mb-2"># 2. Build the examples</div>
<div className="mb-6 text-emerald-400">make examples</div>
<div className="text-zinc-500 mb-2"># 3. Terminal 1: Run the Chatroom Service</div>
<div className="mb-6 text-white">./bin/chatroom_service</div>
<div className="text-zinc-500 mb-2"># 4. Terminal 2: Run the Agent (Edge)</div>
<div className="text-white">./bin/chatroom_agent</div>
</div>
<div className="mt-auto flex gap-4">
<a href="https://github.com/singchia/frontier/tree/main/examples/chatroom" target="_blank" rel="noreferrer" className="flex-1 flex items-center justify-center gap-2 rounded-xl bg-zinc-100 px-4 py-3 text-sm font-semibold text-zinc-900 hover:bg-white transition-all">
<Code2 className="w-4 h-4" /> View Source
</a>
</div>
</div>
</div>
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden flex flex-col group lg:col-span-2">
<div className="bg-zinc-900/50 p-8 border-b border-zinc-800">
<div className="flex items-center justify-between mb-4">
<div className="w-12 h-12 rounded-2xl bg-cyan-500/10 flex items-center justify-center border border-cyan-500/20">
<Radio className="w-6 h-6 text-cyan-400" />
</div>
<span className="px-3 py-1 rounded-full bg-zinc-800 text-xs font-medium text-zinc-300 border border-zinc-700">Stream</span>
</div>
<h2 className="text-2xl font-bold text-white mb-2">
RTMP Relay Demo
</h2>
<p className="text-zinc-400 text-sm">Start here if you care about point-to-point streams, media relay, or using Frontier as a transport for custom traffic.</p>
</div>
<div className="p-8 flex-1 flex flex-col">
<div className="bg-[#111] border border-zinc-800/50 rounded-xl p-5 font-mono text-sm text-zinc-300 mb-8 overflow-x-auto">
<div className="text-zinc-500 mb-2"># 1. Start Frontier Gateway</div>
<div className="mb-6 text-emerald-400">docker run -d -p 30011:30011 -p 30012:30012 singchia/frontier:1.2.2</div>
<div className="text-zinc-500 mb-2"># 2. Build the examples</div>
<div className="mb-6 text-emerald-400">make examples</div>
<div className="text-zinc-500 mb-2"># 3. Terminal 1: Run the RTMP service</div>
<div className="mb-6 text-white">./bin/rtmp_service</div>
<div className="text-zinc-500 mb-2"># 4. Terminal 2: Run the RTMP edge</div>
<div className="text-white">./bin/rtmp_edge</div>
</div>
<div className="mt-auto flex gap-4">
<a href="https://github.com/singchia/frontier/tree/main/examples/rtmp" target="_blank" rel="noreferrer" className="flex-1 flex items-center justify-center gap-2 rounded-xl bg-zinc-100 px-4 py-3 text-sm font-semibold text-zinc-900 hover:bg-white transition-all">
<Code2 className="w-4 h-4" /> View Source
</a>
</div>
</div>
</div>
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden flex flex-col group lg:col-span-2">
<div className="bg-zinc-900/50 p-8 border-b border-zinc-800 flex items-center justify-between gap-6 flex-wrap">
<div>
<h2 className="text-2xl font-bold text-white mb-2">
Which demo should you run first?
</h2>
<p className="text-zinc-400 text-sm">Choose by the job you need Frontier to do, not by the API name.</p>
</div>
<a href="https://github.com/singchia/frontier" target="_blank" rel="noreferrer" className="inline-flex items-center gap-2 rounded-xl border border-zinc-700 px-4 py-3 text-sm font-semibold text-white hover:bg-zinc-900 transition-colors">
Visit GitHub
<ArrowRight className="w-4 h-4" />
</a>
</div>
<div className="grid md:grid-cols-3 gap-px bg-zinc-800">
<div className="bg-[#0D0D0D] p-6">
<div className="text-sm text-blue-400 mb-2">Messaging</div>
<h3 className="text-lg font-bold text-white mb-2">Chatroom</h3>
<p className="text-sm text-zinc-400">For command flow, messaging, edge online/offline state, and the core service-to-edge model.</p>
</div>
<div className="bg-[#0D0D0D] p-6">
<div className="text-sm text-cyan-400 mb-2">Streams</div>
<h3 className="text-lg font-bold text-white mb-2">RTMP</h3>
<p className="text-sm text-zinc-400">For traffic relay, media transport, and understanding how streams differ from RPC and messaging.</p>
</div>
<div className="bg-[#0D0D0D] p-6">
<div className="text-sm text-emerald-400 mb-2">SDK usage</div>
<h3 className="text-lg font-bold text-white mb-2">Docs</h3>
<p className="text-sm text-zinc-400">Once the examples click, move to the usage guide and copy the exact service-side or edge-side SDK pattern.</p>
</div>
</div>
</div>
{/* Video Player */}
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden flex flex-col group">
<div className="bg-zinc-900/50 p-8 border-b border-zinc-800">
<h2 className="text-2xl font-bold text-white mb-2">
Live Demonstration
</h2>
<p className="text-zinc-400 text-sm">Watch the Chatroom example connecting Edge agents with the Service.</p>
</div>
<div className="p-6 flex-1 flex flex-col items-center justify-center bg-[#050505]">
<div className="w-full rounded-2xl overflow-hidden border border-zinc-800 shadow-2xl bg-black aspect-video relative flex items-center justify-center group-hover:border-zinc-700 transition-colors">
<video
controls
preload="metadata"
className="w-full h-full object-cover"
poster="/docs/diagram/frontier.png"
>
<source src="https://github.com/singchia/frontier/assets/15531166/18b01d96-e30b-450f-9610-917d65259c30" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
</div>
</div>
</div>
</div>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+27
View File
@@ -0,0 +1,27 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
:root {
--background: #ffffff;
--foreground: #171717;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
+32
View File
@@ -0,0 +1,32 @@
import { Navbar } from '@/components/Navbar';
import './globals.css';
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Frontier - The Edge-to-Service Communication Gateway',
description: 'Full-duplex bidirectional RPC, messaging, and streams. Built for cloud-native edge environments.',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="dark h-full antialiased scroll-smooth">
<body className="bg-[#0A0A0A] text-zinc-300 min-h-screen flex flex-col font-sans selection:bg-blue-500/30">
<div className="fixed inset-0 z-[-1] bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-900/20 via-[#0A0A0A] to-[#0A0A0A] pointer-events-none"></div>
<Navbar />
<main className="flex-1 pt-20">
{children}
</main>
<footer className="border-t border-white/5 bg-[#0A0A0A] py-12 mt-24">
<div className="max-w-7xl mx-auto px-6 lg:px-8 text-center text-zinc-500 text-sm">
<p className="mb-2">Built with modern cloud-native architectures in mind.</p>
<p>&copy; {new Date().getFullYear()} Frontier. Released under Apache 2.0.</p>
</div>
</footer>
</body>
</html>
);
}
+198
View File
@@ -0,0 +1,198 @@
import Link from 'next/link';
import { ArrowRight, Shield, Activity, Zap, Workflow, CheckCircle2 } from 'lucide-react';
export default function Home() {
return (
<div className="flex flex-col items-center">
{/* Hero Section */}
<section className="relative w-full max-w-7xl mx-auto px-6 lg:px-8 pt-32 pb-24 flex flex-col items-center text-center">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[500px] bg-blue-500/20 blur-[120px] rounded-full pointer-events-none"></div>
<a href="https://github.com/singchia/frontier/releases" target="_blank" rel="noreferrer" className="inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-sm font-medium text-blue-400 bg-blue-500/10 border border-blue-500/20 mb-8 hover:bg-blue-500/20 transition-colors cursor-pointer">
<span className="flex h-2 w-2 rounded-full bg-blue-500 animate-pulse"></span>
Latest stable release: Frontier v1.2.2
<ArrowRight className="w-4 h-4" />
</a>
<h1 className="text-6xl md:text-8xl font-extrabold tracking-tighter text-white mb-8 leading-[1.1]">
Backend services need to <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-cyan-300 to-emerald-300">
reach online edge nodes
</span>
</h1>
<p className="mt-4 text-xl md:text-2xl text-zinc-400 max-w-3xl mx-auto mb-12 font-light leading-relaxed">
Frontier is a service-to-edge gateway for long-lived connections. Use it when backend services and edge nodes both need to actively call, notify, and open streams to each other.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center w-full sm:w-auto">
<a href="https://github.com/singchia/frontier" target="_blank" rel="noreferrer" className="group w-full sm:w-auto rounded-xl bg-zinc-100 px-8 py-4 text-sm font-semibold text-zinc-900 shadow-sm hover:bg-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white transition-all flex items-center justify-center gap-2">
Star on GitHub
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</a>
<Link href="/examples" className="w-full sm:w-auto rounded-xl bg-zinc-900/50 px-8 py-4 text-sm font-semibold text-white hover:bg-zinc-800 border border-zinc-800 transition-all flex items-center justify-center gap-2 backdrop-blur-sm">
Run the 3-minute demos
</Link>
<Link href="/docs" className="w-full sm:w-auto rounded-xl bg-zinc-900/50 px-8 py-4 text-sm font-semibold text-white hover:bg-zinc-800 border border-zinc-800 transition-all flex items-center justify-center gap-2 backdrop-blur-sm">
Read the docs
</Link>
</div>
<div className="mt-8 flex flex-wrap items-center justify-center gap-3 text-xs sm:text-sm text-zinc-400">
<span className="rounded-full border border-zinc-800 bg-zinc-900/60 px-3 py-1.5">Service -&gt; specific edge RPC</span>
<span className="rounded-full border border-zinc-800 bg-zinc-900/60 px-3 py-1.5">Edge -&gt; service callbacks</span>
<span className="rounded-full border border-zinc-800 bg-zinc-900/60 px-3 py-1.5">Messaging + streams on one data plane</span>
</div>
</section>
{/* Terminal Preview Section */}
<section className="w-full max-w-5xl mx-auto px-6 lg:px-8 pb-32">
<div className="rounded-2xl bg-[#0D0D0D] border border-zinc-800 shadow-2xl shadow-black/50 overflow-hidden">
<div className="flex items-center px-4 py-3 border-b border-zinc-800 bg-[#111]">
<div className="flex gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/80"></div>
<div className="w-3 h-3 rounded-full bg-yellow-500/80"></div>
<div className="w-3 h-3 rounded-full bg-green-500/80"></div>
</div>
<div className="mx-auto text-xs text-zinc-500 font-mono">frontier-start.sh</div>
</div>
<div className="p-6 font-mono text-sm leading-relaxed overflow-x-auto">
<div className="flex gap-4">
<span className="text-zinc-600 select-none">1</span>
<span className="text-zinc-400"># Start Frontier and run the fastest demo path</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">2</span>
<span className="text-emerald-400">docker</span> <span className="text-zinc-300">run -d --name frontier \</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">3</span>
<span className="text-zinc-300"> -p 30011:30011</span> <span className="text-zinc-500"># Service-bound port</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">4</span>
<span className="text-zinc-300"> -p 30012:30012</span> <span className="text-zinc-500"># Edge-bound port</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">5</span>
<span className="text-blue-300"> singchia/frontier:1.2.2</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">6</span>
<span className="text-emerald-400">make</span> <span className="text-zinc-300">examples</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">7</span>
<span className="text-zinc-300">./bin/chatroom_service</span> <span className="text-zinc-500"># service side</span>
</div>
<div className="flex gap-4">
<span className="text-zinc-600 select-none">8</span>
<span className="text-zinc-300">./bin/chatroom_agent</span> <span className="text-zinc-500"># edge side</span>
</div>
</div>
</div>
</section>
<section className="w-full max-w-7xl mx-auto px-6 lg:px-8 pb-24">
<div className="grid lg:grid-cols-2 gap-6">
<div className="rounded-3xl border border-emerald-500/20 bg-emerald-500/[0.04] p-8">
<h2 className="text-2xl font-bold text-white mb-5">Use Frontier when</h2>
<div className="space-y-4">
{[
'A backend service needs to call a specific online device, agent, or connector',
'Edge nodes need to call backend services without opening inbound ports',
'You need RPC, messaging, and streams on the same long-lived connection model',
'Your system is service <-> edge, not just service <-> service',
].map((item) => (
<div key={item} className="flex items-start gap-3 text-zinc-300">
<CheckCircle2 className="w-5 h-5 text-emerald-400 mt-0.5 flex-shrink-0" />
<span>{item}</span>
</div>
))}
</div>
</div>
<div className="rounded-3xl border border-zinc-800 bg-zinc-900/40 p-8">
<h2 className="text-2xl font-bold text-white mb-5">Do not use Frontier when</h2>
<div className="space-y-4 text-zinc-400">
<p>You only need service-to-service RPC. Use gRPC.</p>
<p>You only need HTTP ingress or proxying. Use Envoy or an API gateway.</p>
<p>You only need pub/sub or event streaming. Use NATS or Kafka.</p>
<p>You only need a generic tunnel. Use frp or another tunnel tool.</p>
</div>
</div>
</div>
</section>
{/* Core Features */}
<section className="w-full max-w-7xl mx-auto px-6 lg:px-8 py-24 border-t border-zinc-800/50">
<div className="text-center mb-16">
<h2 className="text-3xl md:text-5xl font-bold text-white tracking-tight mb-4">One connection model, three primitives</h2>
<p className="text-zinc-400 text-lg">The reason Frontier feels different is that RPC, messaging, and streams are part of the same service-to-edge model.</p>
</div>
<div className="grid md:grid-cols-3 gap-6">
<div className="group rounded-3xl bg-zinc-900/40 border border-zinc-800/50 p-8 hover:bg-zinc-800/50 hover:border-zinc-700 transition-all">
<div className="w-12 h-12 rounded-2xl bg-blue-500/10 flex items-center justify-center mb-6 group-hover:scale-110 group-hover:bg-blue-500/20 transition-all">
<Activity className="w-6 h-6 text-blue-400" />
</div>
<h3 className="text-xl font-bold text-white mb-3">Bidirectional RPC</h3>
<p className="text-zinc-400 leading-relaxed text-sm">
Address a specific online edge node from a backend service, or let edge nodes call backend services back over the same communication model.
</p>
</div>
<div className="group rounded-3xl bg-zinc-900/40 border border-zinc-800/50 p-8 hover:bg-zinc-800/50 hover:border-zinc-700 transition-all">
<div className="w-12 h-12 rounded-2xl bg-purple-500/10 flex items-center justify-center mb-6 group-hover:scale-110 group-hover:bg-purple-500/20 transition-all">
<Workflow className="w-6 h-6 text-purple-400" />
</div>
<h3 className="text-xl font-bold text-white mb-3">Topic Messaging</h3>
<p className="text-zinc-400 leading-relaxed text-sm">
Push telemetry, events, and notifications between services and edges, with explicit acknowledgments and optional forwarding to external MQ.
</p>
</div>
<div className="group rounded-3xl bg-zinc-900/40 border border-zinc-800/50 p-8 hover:bg-zinc-800/50 hover:border-zinc-700 transition-all">
<div className="w-12 h-12 rounded-2xl bg-cyan-500/10 flex items-center justify-center mb-6 group-hover:scale-110 group-hover:bg-cyan-500/20 transition-all">
<Zap className="w-6 h-6 text-cyan-400" />
</div>
<h3 className="text-xl font-bold text-white mb-3">P2P Multiplexing</h3>
<p className="text-zinc-400 leading-relaxed text-sm">
Open direct streams for proxying, file transfer, media relay, or custom protocols when RPC is not enough.
</p>
</div>
</div>
</section>
{/* Deployment */}
<section className="w-full border-t border-zinc-800/50 bg-zinc-900/20 py-24">
<div className="max-w-7xl mx-auto px-6 lg:px-8 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-white mb-6">Cloud-Native by Design</h2>
<p className="text-zinc-400 max-w-2xl mx-auto mb-16 text-lg">Start with a single container, then move to clustered deployment when your service-to-edge fleet grows.</p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto">
<div className="px-6 py-8 bg-[#0A0A0A] border border-zinc-800 rounded-2xl hover:border-zinc-700 transition-colors">
<span className="text-white font-mono font-medium block mb-2 text-lg">Docker</span>
<span className="text-sm text-zinc-500">Standalone container</span>
</div>
<div className="px-6 py-8 bg-[#0A0A0A] border border-zinc-800 rounded-2xl hover:border-zinc-700 transition-colors">
<span className="text-white font-mono font-medium block mb-2 text-lg">Compose</span>
<span className="text-sm text-zinc-500">Local cluster</span>
</div>
<div className="px-6 py-8 bg-[#0A0A0A] border border-zinc-800 rounded-2xl hover:border-zinc-700 transition-colors">
<span className="text-white font-mono font-medium block mb-2 text-lg">Helm</span>
<span className="text-sm text-zinc-500">K8s deployment</span>
</div>
<div className="px-6 py-8 bg-[#0A0A0A] border border-zinc-800 rounded-2xl hover:border-zinc-700 transition-colors relative overflow-hidden">
<div className="absolute top-0 right-0 p-2">
<Shield className="w-4 h-4 text-emerald-500/50" />
</div>
<span className="text-white font-mono font-medium block mb-2 text-lg">Operator</span>
<span className="text-sm text-zinc-500">HA & Scale</span>
</div>
</div>
</div>
</section>
</div>
);
}
+215
View File
@@ -0,0 +1,215 @@
import { Server, Smartphone, Zap, FileUp, Workflow, Radio } from 'lucide-react';
export default function UseCases() {
return (
<div className="w-full max-w-7xl mx-auto px-6 lg:px-8 py-24">
<div className="max-w-3xl mb-20">
<h1 className="text-4xl md:text-6xl font-extrabold tracking-tighter text-white mb-6">
Built for real-world <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-cyan-300">edge challenges</span>
</h1>
<p className="text-xl text-zinc-400 leading-relaxed font-light">
See how Frontier solves complex communication patterns that traditional gateways struggle with.
</p>
</div>
<div className="space-y-16">
{/* Case 1: Service-to-Client RPC */}
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden hover:border-zinc-700 transition-colors">
<div className="grid md:grid-cols-2">
<div className="p-10 md:p-12 flex flex-col justify-center">
<div className="w-12 h-12 rounded-2xl bg-blue-500/10 flex items-center justify-center mb-6 border border-blue-500/20">
<Zap className="w-6 h-6 text-blue-400" />
</div>
<h2 className="text-2xl font-bold text-white mb-4">Command & Control (RPC)</h2>
<p className="text-zinc-400 mb-8 leading-relaxed">
In traditional setups, clients poll servers for commands. With Frontier, the microservice can issue a direct RPC call to a specific Edge node by its ID. Perfect for IoT device management, game server commands, or remote agent execution.
</p>
<div className="bg-[#111] border border-zinc-800/50 rounded-xl p-5 font-mono text-sm">
<div className="text-zinc-500 mb-2"># Call a method on a specific edge node</div>
<div className="text-blue-300">response, err := frontier.Call(</div>
<div className="text-zinc-300 pl-4">edgeID,</div>
<div className="text-emerald-300 pl-4">&quot;RebootDevice&quot;,</div>
<div className="text-zinc-300 pl-4">payload,</div>
<div className="text-blue-300">)</div>
</div>
</div>
{/* Visual representation of RPC */}
<div className="bg-[#111] p-10 flex items-center justify-center border-l border-zinc-800">
<div className="w-full max-w-sm">
<div className="flex items-center justify-between mb-8">
<div className="flex flex-col items-center gap-2">
<div className="w-16 h-16 rounded-2xl bg-blue-500/20 border border-blue-500/30 flex items-center justify-center shadow-[0_0_30px_rgba(59,130,246,0.15)] z-10">
<Server className="w-8 h-8 text-blue-400" />
</div>
<span className="text-xs text-zinc-500 font-mono">Microservice</span>
</div>
<div className="flex-1 relative flex items-center justify-center px-4">
<div className="w-full h-[2px] bg-zinc-800 absolute"></div>
<div className="w-full h-[2px] bg-gradient-to-r from-blue-500 to-emerald-400 absolute animate-[pulse_2s_ease-in-out_infinite]"></div>
<div className="bg-[#111] px-3 z-10 text-xs text-zinc-400 font-mono border border-zinc-800 rounded-full">RPC.Call</div>
</div>
<div className="flex flex-col items-center gap-2">
<div className="w-16 h-16 rounded-2xl bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center shadow-[0_0_30px_rgba(16,185,129,0.15)] z-10">
<Smartphone className="w-8 h-8 text-emerald-400" />
</div>
<span className="text-xs text-zinc-500 font-mono">Edge Node</span>
</div>
</div>
<div className="bg-zinc-900 border border-zinc-800 rounded-xl p-4 text-xs font-mono text-zinc-400">
<div className="flex justify-between border-b border-zinc-800 pb-2 mb-2">
<span>Target:</span>
<span className="text-white">Edge-X792</span>
</div>
<div className="flex justify-between">
<span>Method:</span>
<span className="text-emerald-400">RebootDevice</span>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Case 2: Edge Telemetry */}
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden hover:border-zinc-700 transition-colors">
<div className="grid md:grid-cols-2">
{/* Visual representation of Messaging */}
<div className="bg-[#111] p-10 flex items-center justify-center border-r border-zinc-800 order-2 md:order-1">
<div className="w-full max-w-xs">
{/* Publisher */}
<div className="flex items-center gap-3 mb-2">
<div className="w-10 h-10 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center shadow-[0_0_20px_rgba(168,85,247,0.15)] relative flex-shrink-0">
<Smartphone className="w-5 h-5 text-purple-400" />
<div className="absolute -top-1 -right-1 w-2.5 h-2.5 bg-emerald-500 rounded-full border-2 border-[#111]"></div>
</div>
<div>
<div className="text-xs font-mono text-zinc-200">Edge Node</div>
<div className="text-[10px] font-mono text-zinc-500">Publisher</div>
</div>
</div>
{/* Arrow down + label */}
<div className="flex items-center gap-2 ml-5 mb-2">
<div className="flex flex-col items-center">
<div className="w-[2px] h-4 bg-gradient-to-b from-purple-500 to-purple-500/30 rounded-full"></div>
<div className="w-0 h-0 border-l-[3px] border-r-[3px] border-t-[5px] border-l-transparent border-r-transparent border-t-purple-400"></div>
</div>
<span className="text-[10px] font-mono text-zinc-500 bg-zinc-900 px-2 py-0.5 rounded border border-zinc-800">Publish(&quot;sensor/tmp&quot;, data)</span>
</div>
{/* Frontier broker box */}
<div className="rounded-xl border border-zinc-700 bg-zinc-900/80 p-3 mb-2 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/5 to-transparent pointer-events-none"></div>
<div className="flex items-center gap-2 mb-1">
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full animate-[pulse_2s_ease-in-out_infinite]"></div>
<span className="text-[10px] font-mono text-zinc-400 uppercase tracking-widest">Frontier Broker</span>
</div>
<div className="flex items-center gap-2 mt-1">
<div className="bg-purple-500/10 border border-purple-500/20 rounded-lg px-3 py-1.5 flex-1 text-center">
<div className="text-[9px] text-zinc-500 font-mono leading-none mb-0.5">TOPIC</div>
<div className="text-[11px] font-mono text-purple-300">sensor/tmp</div>
</div>
<div className="text-zinc-600 text-xs"></div>
<div className="text-[10px] font-mono text-zinc-400 flex flex-col gap-0.5">
<div className="bg-zinc-800 rounded px-1.5 py-0.5 text-zinc-300">route</div>
</div>
</div>
</div>
{/* Fan-out arrows + subscribers */}
<div className="ml-5 flex flex-col gap-2">
{/* Subscriber 1 */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-0.5">
<div className="w-3 h-[2px] bg-blue-500/50"></div>
<div className="w-0 h-0 border-l-[4px] border-y-[3px] border-l-blue-400 border-y-transparent"></div>
</div>
<div className="flex items-center gap-2 bg-zinc-900/80 border border-zinc-800 hover:border-blue-500/30 transition-colors rounded-lg px-3 py-2 flex-1">
<div className="p-1 rounded bg-blue-500/10">
<Server className="w-3 h-3 text-blue-400" />
</div>
<span className="text-[11px] font-mono text-zinc-300">Service A</span>
<span className="ml-auto text-[9px] font-mono text-blue-500/70">subscriber</span>
</div>
</div>
{/* Subscriber 2 */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-0.5">
<div className="w-3 h-[2px] bg-amber-500/50"></div>
<div className="w-0 h-0 border-l-[4px] border-y-[3px] border-l-amber-400 border-y-transparent"></div>
</div>
<div className="flex items-center gap-2 bg-zinc-900/80 border border-zinc-800 hover:border-amber-500/30 transition-colors rounded-lg px-3 py-2 flex-1">
<div className="p-1 rounded bg-amber-500/10">
<Radio className="w-3 h-3 text-amber-400" />
</div>
<div className="flex flex-col leading-none">
<span className="text-[11px] font-mono text-zinc-300">Kafka MQ</span>
<span className="text-[9px] font-mono text-zinc-500">forward</span>
</div>
<span className="ml-auto text-[9px] font-mono text-amber-500/70">external</span>
</div>
</div>
</div>
</div>
</div>
<div className="p-10 md:p-12 flex flex-col justify-center order-1 md:order-2">
<div className="w-12 h-12 rounded-2xl bg-purple-500/10 flex items-center justify-center mb-6 border border-purple-500/20">
<Workflow className="w-6 h-6 text-purple-400" />
</div>
<h2 className="text-2xl font-bold text-white mb-4">Edge Telemetry & Routing</h2>
<p className="text-zinc-400 mb-8 leading-relaxed">
Edge nodes publish telemetry or events to Topics. Frontier intelligently routes these to the appropriate backend microservice or forwards them directly to an external MQ (like Kafka), ensuring guaranteed delivery with explicit ACKs.
</p>
<div className="bg-[#111] border border-zinc-800/50 rounded-xl p-5 font-mono text-sm">
<div className="text-zinc-500 mb-2"># Edge publishes to a topic</div>
<div className="text-purple-300">frontier.Publish(</div>
<div className="text-emerald-300 pl-4">&quot;sensor/temperature&quot;,</div>
<div className="text-zinc-300 pl-4">[]byte(`&#123;&quot;temp&quot;: 42.5&#125;`),</div>
<div className="text-purple-300">)</div>
</div>
</div>
</div>
</div>
{/* Case 3: Streaming */}
<div className="bg-[#0D0D0D] border border-zinc-800 rounded-3xl overflow-hidden hover:border-zinc-700 transition-colors">
<div className="grid md:grid-cols-2">
<div className="p-10 md:p-12 flex flex-col justify-center">
<div className="w-12 h-12 rounded-2xl bg-cyan-500/10 flex items-center justify-center mb-6 border border-cyan-500/20">
<FileUp className="w-6 h-6 text-cyan-400" />
</div>
<h2 className="text-2xl font-bold text-white mb-4">Traffic Proxying & Files</h2>
<p className="text-zinc-400 mb-8 leading-relaxed">
Need to transfer large files or proxy custom protocol traffic (like SSH/VNC/RTMP) to a device behind a NAT? Open a point-to-point stream. The stream acts as a raw multiplexed socket, bypassing all routing overhead.
</p>
<div className="bg-[#111] border border-zinc-800/50 rounded-xl p-5 font-mono text-sm">
<div className="text-zinc-500 mb-2"># Open a raw byte stream to edge</div>
<div className="text-zinc-300">stream, err := <span className="text-cyan-300">frontier.OpenStream(</span>edgeID<span className="text-cyan-300">)</span></div>
<div className="text-zinc-500 mt-4 mb-2"># Stream a file or pipe SSH directly</div>
<div className="text-zinc-300">io.Copy(stream, largeFile)</div>
</div>
</div>
{/* Visual representation of Stream */}
<div className="bg-[#111] p-10 flex items-center justify-center border-l border-zinc-800 relative overflow-hidden">
<img
src="/docs/diagram/stream.png"
alt="Frontier Stream Flow"
className="w-full max-w-sm rounded-xl border border-zinc-800 shadow-2xl relative z-10 mix-blend-screen opacity-90"
/>
{/* Background ambient glow matching the image */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[200px] h-[200px] bg-cyan-500/20 blur-[80px] rounded-full pointer-events-none z-0"></div>
</div>
</div>
</div>
</div>
</div>
);
}
+128
View File
@@ -0,0 +1,128 @@
import { CheckCircle2, XCircle, Zap, Cpu, Network, Server } from 'lucide-react';
export default function WhyFrontier() {
return (
<div className="w-full max-w-7xl mx-auto px-6 lg:px-8 py-24">
<div className="max-w-3xl mb-24">
<div className="inline-flex items-center gap-2 rounded-full px-4 py-1.5 text-sm font-medium text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 mb-6">
<Zap className="w-4 h-4" /> The structural advantage
</div>
<h1 className="text-4xl md:text-6xl font-extrabold tracking-tighter text-white mb-6">
Why build with Frontier?
</h1>
<p className="text-xl text-zinc-400 leading-relaxed font-light">
Understand the fundamental differences between Frontier and traditional reverse proxies or message queues, and see why it excels in edge computing scenarios.
</p>
</div>
{/* Comparison Table */}
<section className="mb-32">
<div className="flex items-center gap-4 mb-8">
<div className="w-10 h-10 rounded-xl bg-zinc-800 flex items-center justify-center border border-zinc-700">
<svg className="w-5 h-5 text-zinc-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
</div>
<h2 className="text-2xl md:text-3xl font-bold text-white tracking-tight">Structural Differences</h2>
</div>
<div className="overflow-x-auto rounded-3xl border border-zinc-800 bg-[#0D0D0D] shadow-2xl">
<table className="w-full text-left border-collapse min-w-[800px]">
<thead>
<tr className="border-b border-zinc-800 bg-zinc-900/50">
<th className="py-6 px-8 text-sm font-medium text-zinc-400 w-1/4">Capability</th>
<th className="py-6 px-8 text-sm font-semibold text-blue-400 bg-blue-950/20 w-1/4">Frontier</th>
<th className="py-6 px-8 text-sm font-medium text-zinc-500 w-1/4">Reverse Proxy (Nginx/Envoy)</th>
<th className="py-6 px-8 text-sm font-medium text-zinc-500 w-1/4">Message Queue (Kafka/RabbitMQ)</th>
</tr>
</thead>
<tbody className="divide-y divide-zinc-800/50 text-sm">
<tr className="hover:bg-zinc-900/30 transition-colors">
<td className="py-6 px-8 font-medium text-white">Full-Duplex Native</td>
<td className="py-6 px-8 bg-blue-950/10">
<div className="flex items-center text-blue-400 font-medium"><CheckCircle2 className="w-5 h-5 mr-3" /> Yes (Bi-directional RPC)</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Client to Server only</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Pub/Sub only, no RPC</div>
</td>
</tr>
<tr className="hover:bg-zinc-900/30 transition-colors">
<td className="py-6 px-8 font-medium text-white">Edge Node Presence</td>
<td className="py-6 px-8 bg-blue-950/10">
<div className="flex items-center text-blue-400 font-medium"><CheckCircle2 className="w-5 h-5 mr-3" /> Built-in Online State</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Stateless</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Topic-based only</div>
</td>
</tr>
<tr className="hover:bg-zinc-900/30 transition-colors">
<td className="py-6 px-8 font-medium text-white">Point-to-Point Stream</td>
<td className="py-6 px-8 bg-blue-950/10">
<div className="flex items-center text-blue-400 font-medium"><CheckCircle2 className="w-5 h-5 mr-3" /> Direct multiplexed streams</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Proxy forwarding only</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Not supported</div>
</td>
</tr>
<tr className="hover:bg-zinc-900/30 transition-colors">
<td className="py-6 px-8 font-medium text-white">Control Plane API</td>
<td className="py-6 px-8 bg-blue-950/10">
<div className="flex items-center text-blue-400 font-medium"><CheckCircle2 className="w-5 h-5 mr-3" /> Query online nodes & state</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Config management only</div>
</td>
<td className="py-6 px-8">
<div className="flex items-center text-zinc-500"><XCircle className="w-5 h-5 mr-3" /> Consumer group metrics only</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
{/* Target Scenarios */}
<section>
<div className="flex items-center gap-4 mb-12">
<div className="w-10 h-10 rounded-xl bg-zinc-800 flex items-center justify-center border border-zinc-700">
<svg className="w-5 h-5 text-zinc-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path></svg>
</div>
<h2 className="text-2xl md:text-3xl font-bold text-white tracking-tight">Purpose-built for Edge Scenarios</h2>
</div>
<div className="grid md:grid-cols-2 gap-6">
<div className="bg-[#0D0D0D] border border-zinc-800 p-8 rounded-3xl hover:border-zinc-700 transition-colors group">
<Cpu className="w-8 h-8 text-blue-400 mb-6 group-hover:scale-110 transition-transform" />
<h3 className="text-xl font-bold text-white mb-3">IoT & Edge Devices</h3>
<p className="text-zinc-400 leading-relaxed">Maintain massive numbers of long connections. Services need to push commands to specific devices (RPC) and devices need to upload telemetry (Messaging) efficiently.</p>
</div>
<div className="bg-[#0D0D0D] border border-zinc-800 p-8 rounded-3xl hover:border-zinc-700 transition-colors group">
<Network className="w-8 h-8 text-purple-400 mb-6 group-hover:scale-110 transition-transform" />
<h3 className="text-xl font-bold text-white mb-3">Remote Access & Tunneling</h3>
<p className="text-zinc-400 leading-relaxed">Need to expose a service behind NAT? Devices connect out to Frontier, and services can open a point-to-point stream to the device safely behind the firewall.</p>
</div>
<div className="bg-[#0D0D0D] border border-zinc-800 p-8 rounded-3xl hover:border-zinc-700 transition-colors group">
<Zap className="w-8 h-8 text-amber-400 mb-6 group-hover:scale-110 transition-transform" />
<h3 className="text-xl font-bold text-white mb-3">Realtime Chat & Sync</h3>
<p className="text-zinc-400 leading-relaxed">Clients maintain persistent presence. Services can route messages to specific clients based on Edge ID, with guaranteed delivery acknowledgment built into the protocol.</p>
</div>
<div className="bg-[#0D0D0D] border border-zinc-800 p-8 rounded-3xl hover:border-zinc-700 transition-colors group">
<Server className="w-8 h-8 text-emerald-400 mb-6 group-hover:scale-110 transition-transform" />
<h3 className="text-xl font-bold text-white mb-3">Multi-tenant Agents</h3>
<p className="text-zinc-400 leading-relaxed">Deploying agents into customer VPCs? Use Frontier as the central control plane where all agents connect, allowing cloud services to manage them securely without exposing internal ports.</p>
</div>
</div>
</section>
</div>
);
}
+78
View File
@@ -0,0 +1,78 @@
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
type DocLink = {
href: string;
label: string;
external?: boolean;
};
type DocSection = {
title: string;
links: DocLink[];
};
const navItems: DocSection[] = [
{
title: 'Getting Started',
links: [
{ href: '/docs', label: 'Overview & Quick Start' },
{ href: '/docs/usage', label: 'Usage Guide' },
{ href: '/docs/configuration', label: 'Configuration' },
]
},
{
title: 'Deployment',
links: [
{ href: '/docs/cluster', label: 'Cluster Architecture' },
{ href: '/docs/operator', label: 'Kubernetes Operator' },
]
},
{
title: 'API Reference',
links: [
{ href: 'https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/service', label: 'Service API', external: true },
{ href: 'https://pkg.go.dev/github.com/singchia/frontier/api/dataplane/v1/edge', label: 'Edge API', external: true },
]
}
];
export function DocsSidebar() {
const pathname = usePathname();
return (
<nav className="sticky top-28 space-y-8 pr-6">
{navItems.map((section) => (
<div key={section.title}>
<h4 className="font-semibold text-white mb-3 uppercase tracking-wider text-xs">{section.title}</h4>
<ul className="space-y-1.5 text-sm">
{section.links.map((link) => {
const isActive = pathname === link.href;
return (
<li key={link.href}>
<Link
href={link.href}
target={link.external ? '_blank' : undefined}
className={`flex items-center py-1.5 px-3 -mx-3 rounded-lg transition-colors ${
isActive
? 'bg-blue-500/10 text-blue-400 font-medium'
: 'text-zinc-400 hover:text-white hover:bg-zinc-800/50'
}`}
>
{link.label}
{link.external && (
<svg className="w-3 h-3 ml-1.5 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
)}
</Link>
</li>
);
})}
</ul>
</div>
))}
</nav>
);
}
+45
View File
@@ -0,0 +1,45 @@
import Link from 'next/link';
export function Navbar() {
return (
<nav className="fixed top-0 w-full z-50 bg-[#0A0A0A]/80 backdrop-blur-xl border-b border-white/[0.08] supports-[backdrop-filter]:bg-[#0A0A0A]/60">
<div className="max-w-7xl mx-auto px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center gap-8">
<Link href="/" className="flex items-center gap-2 group">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-400 flex items-center justify-center shadow-lg shadow-blue-500/20 group-hover:shadow-blue-500/40 transition-shadow">
<span className="text-white font-bold text-sm tracking-tighter">F</span>
</div>
<span className="text-lg font-semibold text-zinc-100 tracking-tight">Frontier</span>
</Link>
<div className="hidden md:flex items-center gap-6">
<Link href="/why-frontier" className="text-sm font-medium text-zinc-400 hover:text-zinc-100 transition-colors">
Why Frontier
</Link>
<Link href="/architecture" className="text-sm font-medium text-zinc-400 hover:text-zinc-100 transition-colors">
Architecture
</Link>
<Link href="/use-cases" className="text-sm font-medium text-zinc-400 hover:text-zinc-100 transition-colors">
Use Cases
</Link>
<Link href="/examples" className="text-sm font-medium text-zinc-400 hover:text-zinc-100 transition-colors">
Examples
</Link>
<Link href="/docs" className="text-sm font-medium text-zinc-400 hover:text-zinc-100 transition-colors">
Documentation
</Link>
</div>
</div>
<div className="flex items-center gap-4">
<a href="https://github.com/singchia/frontier" target="_blank" rel="noreferrer" className="text-zinc-400 hover:text-zinc-100 transition-colors flex items-center gap-2 text-sm font-medium">
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
</svg>
<span className="hidden sm:inline">Star on GitHub</span>
</a>
</div>
</div>
</div>
</nav>
);
}
+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}