Skip to content

Commit 0138706

Browse files
author
yosebyte
authored
feat(master): implement core master functionality with REST API
- Added master.go to handle the main logic and server setup. - Implemented periodic tasks for state backup and instance management in periodic.go. - Created RESTful endpoints for instance management in rest.go, including handling GET, POST, PATCH, PUT, and DELETE requests. - Introduced state management with save and load functionality in state.go. - Added system information retrieval in sysinfo.go to provide metrics about the host. - Implemented TLS configuration and management in tls.go for secure communications. - Defined data structures and types in types.go for instances, events, and metadata. - Added utility functions for JSON responses and ID generation in util.go.
1 parent ba03275 commit 0138706

20 files changed

Lines changed: 3463 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: [ 'v*.*.*' ]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v6
15+
- uses: actions/setup-go@v6
16+
with:
17+
go-version-file: go.mod
18+
- uses: goreleaser/goreleaser-action@v7
19+
with:
20+
version: "~> v2"
21+
args: release --clean
22+
env:
23+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

.goreleaser.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
version: 2
2+
3+
project_name: openctrl
4+
5+
builds:
6+
- env:
7+
- CGO_ENABLED=0
8+
main: ./cmd/openctrl
9+
goos:
10+
- darwin
11+
- freebsd
12+
- linux
13+
- windows
14+
goarch:
15+
- amd64
16+
- arm64
17+
flags:
18+
- -trimpath
19+
ldflags:
20+
- -s -w -X main.version={{ .Tag }}

LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2026, NodePassProject
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 264 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,264 @@
1-
# OpenCtrl
1+
<p align="center">
2+
<img src="assets/openctrl.png" alt="OpenCtrl banner" width="50%">
3+
</p>
4+
5+
<p align="center">
6+
<a href="docs/master.md"><img alt="API v2" src="https://img.shields.io/badge/API-v2-14b8a6?style=flat-square"></a>
7+
<a href="docs/master.md"><img alt="Events SSE" src="https://img.shields.io/badge/events-SSE-f59e0b?style=flat-square"></a>
8+
<img alt="Go 1.26.4" src="https://img.shields.io/badge/Go-1.26.4-00ADD8?style=flat-square&amp;logo=go&amp;logoColor=white">
9+
<a href="LICENSE"><img alt="License BSD 3-Clause" src="https://img.shields.io/badge/license-BSD--3--Clause-0f172a?style=flat-square"></a>
10+
</p>
11+
12+
OpenCtrl is a focused control-plane master for supervising URL-defined
13+
instances. It stores instance definitions, launches a compatible runtime binary
14+
for each managed instance, exposes a versioned REST API, and streams live
15+
lifecycle, log, and metric updates over Server-Sent Events.
16+
17+
It is intentionally narrow: the master owns orchestration, persistence,
18+
authentication, and observability; runtime binaries own protocol-specific work.
19+
20+
## Contents
21+
22+
- [Highlights](#highlights)
23+
- [Architecture](#architecture)
24+
- [Quick Start](#quick-start)
25+
- [Master URL](#master-url)
26+
- [API Surface](#api-surface)
27+
- [Runtime Contract](#runtime-contract)
28+
- [State and Security](#state-and-security)
29+
- [Release](#release)
30+
- [Documentation](#documentation)
31+
32+
## Highlights
33+
34+
| Capability | What it provides |
35+
| --- | --- |
36+
| URL-defined instances | Every managed workload is represented by one URL and one generated instance ID. |
37+
| Process supervision | The master starts, stops, restarts, and deletes child processes through a stable runtime contract. |
38+
| Versioned control API | REST endpoints are served under `/api/v2` or a custom versioned prefix. |
39+
| Live event stream | SSE publishes initial state, lifecycle changes, child logs, metric checkpoints, deletions, and shutdown events. |
40+
| Durable local state | Instance definitions and the API key are persisted beside the executable with a backup file. |
41+
| Runtime neutrality | The master does not special-case child protocols; compatible binaries validate and execute their own URLs. |
42+
| TLS-ready operation | HTTP, self-signed TLS, and file-backed TLS certificate modes are selected from the master URL. |
43+
44+
## Architecture
45+
46+
```mermaid
47+
flowchart LR
48+
client["API clients"]
49+
events["SSE subscribers"]
50+
master["OpenCtrl master"]
51+
state["gob state"]
52+
runtime["runtime binary"]
53+
instA["instance URL"]
54+
instB["instance URL"]
55+
56+
client -->|"REST + X-API-Key"| master
57+
master -->|"event: instance"| events
58+
master <-->|"load / save"| state
59+
master -->|"spawn <runtime> <url>"| runtime
60+
runtime --> instA
61+
runtime --> instB
62+
```
63+
64+
The master process provides the control plane. Child processes stay in the
65+
foreground, emit logs and optional checkpoints, and exit when the master stops
66+
them.
67+
68+
## Quick Start
69+
70+
Build the master:
71+
72+
```sh
73+
go build -trimpath -o ./bin/openctrl ./cmd/openctrl
74+
```
75+
76+
Start a local control plane:
77+
78+
```sh
79+
./bin/openctrl 'master://127.0.0.1:8080'
80+
```
81+
82+
The master prints an API key on startup:
83+
84+
```text
85+
Master.run: API key created: <api-key>
86+
```
87+
88+
Use the key with the versioned API:
89+
90+
```sh
91+
BASE='http://127.0.0.1:8080/api/v2'
92+
API_KEY='<api-key>'
93+
94+
curl -H "X-API-Key: ${API_KEY}" "${BASE}/info"
95+
curl -H "X-API-Key: ${API_KEY}" "${BASE}/instances"
96+
```
97+
98+
Run managed instances through a compatible runtime binary:
99+
100+
```sh
101+
./bin/openctrl 'master://127.0.0.1:8080?bin=/opt/openctrl/runtime'
102+
```
103+
104+
Create an instance:
105+
106+
```sh
107+
curl -X POST "${BASE}/instances" \
108+
-H "X-API-Key: ${API_KEY}" \
109+
-H "Content-Type: application/json" \
110+
-d '{"alias":"edge-a","url":"managed://edge-a"}'
111+
```
112+
113+
## Master URL
114+
115+
```text
116+
master://<host>:<port>[/prefix][?<query>]
117+
```
118+
119+
The host and port become the listen address. The optional path becomes the API
120+
prefix, and the API version is appended automatically.
121+
122+
| Master URL | API base |
123+
| --- | --- |
124+
| `master://127.0.0.1:8080` | `http://127.0.0.1:8080/api/v2` |
125+
| `master://127.0.0.1:8080/control` | `http://127.0.0.1:8080/control/v2` |
126+
| `master://127.0.0.1:8443?tls=1` | `https://127.0.0.1:8443/api/v2` |
127+
128+
| Parameter | Values | Default | Purpose |
129+
| --- | --- | --- | --- |
130+
| `tls` | `0`, `1`, `2` | `0` | Select HTTP, self-signed TLS, or certificate-backed TLS. |
131+
| `crt` | file path | empty | Certificate file used with `tls=2`. |
132+
| `key` | file path | empty | Private key file used with `tls=2`. |
133+
| `bin` | file path | current executable | Runtime binary used to launch managed instances. |
134+
135+
## API Surface
136+
137+
All protected requests must include:
138+
139+
```text
140+
X-API-Key: <api-key>
141+
```
142+
143+
Core endpoints:
144+
145+
| Method | Path | Description |
146+
| --- | --- | --- |
147+
| `GET` | `/info` | Return master identity, runtime configuration, uptime, and system metrics. |
148+
| `POST` | `/info` | Set the master alias. |
149+
| `GET` | `/instances` | Return all instances, including the internal API-key instance. |
150+
| `POST` | `/instances` | Create and asynchronously start an instance. |
151+
| `GET` | `/instances/{id}` | Return one instance. |
152+
| `PATCH` | `/instances/{id}` | Update metadata, restart policy, or lifecycle action. |
153+
| `PUT` | `/instances/{id}` | Replace the instance URL and restart the instance. |
154+
| `DELETE` | `/instances/{id}` | Stop and delete the instance. |
155+
| `GET` | `/events` | Open the Server-Sent Events stream. |
156+
| `GET` | `/tcping?target=host:port` | Test TCP reachability from the master process. |
157+
158+
Lifecycle actions accepted by `PATCH /instances/{id}`:
159+
160+
| Action | Effect |
161+
| --- | --- |
162+
| `start` | Start a stopped child process. |
163+
| `stop` | Stop an active child process. |
164+
| `restart` | Stop and start the child process. |
165+
| `reset` | Reset runtime byte counters. |
166+
167+
SSE frames use the event name `instance`:
168+
169+
```text
170+
event: instance
171+
data: {"type":"update","time":"2026-06-08T12:00:00Z","instance":{...},"logs":""}
172+
```
173+
174+
Event payload types are `initial`, `create`, `update`, `delete`, `log`, and
175+
`shutdown`.
176+
177+
## Runtime Contract
178+
179+
OpenCtrl supervises runtime binaries with a small process contract:
180+
181+
```text
182+
<runtime-binary> <instance-url>
183+
```
184+
185+
For non-`master` instance schemes, point `bin` at a binary that understands
186+
those schemes. The master treats child URLs as opaque runtime configuration.
187+
188+
Runtime binaries MUST:
189+
190+
- accept the full instance URL as the first positional argument;
191+
- reject invalid configuration before starting long-running work;
192+
- stay in the foreground while the instance is active;
193+
- handle termination and exit within the master's 5-second grace period when
194+
possible;
195+
- return a non-zero exit code for failures that should place the instance in
196+
`error`;
197+
- avoid printing secrets from URLs, headers, keys, or tokens.
198+
199+
Runtime binaries SHOULD:
200+
201+
- emit newline-delimited logs to stdout or stderr;
202+
- emit checkpoints after the service is ready when metrics are available;
203+
- keep byte counters monotonic for the life of the process.
204+
205+
Runtime binaries MAY:
206+
207+
- define their own URL schemes and query parameters;
208+
- omit checkpoints when metrics are not meaningful;
209+
- print ordinary operational logs that should be visible to API clients.
210+
211+
Checkpoint format:
212+
213+
```text
214+
CHECK_POINT|MODE=<n>|PING=<n>ms|POOL=<n>|TCPS=<n>|UDPS=<n>|TCPRX=<bytes>|TCPTX=<bytes>|UDPRX=<bytes>|UDPTX=<bytes>
215+
```
216+
217+
Non-checkpoint child output is forwarded to stdout and published as `log`
218+
events. If a line contains the substring `ERROR`, the instance is marked
219+
`error`.
220+
221+
## State and Security
222+
223+
State is stored beside the executable:
224+
225+
```text
226+
<executable-directory>/gob/openctrl.gob
227+
<executable-directory>/gob/openctrl.gob.backup
228+
```
229+
230+
Operational notes:
231+
232+
- protect the API key printed by the master;
233+
- protect the state directory because `openctrl.gob` stores the API key;
234+
- use `tls=2` with a trusted certificate outside local-only deployments;
235+
- use a trusted `bin` path because managed instances execute that binary;
236+
- treat instance URLs as secrets when they contain credentials or tokens;
237+
- account for permissive CORS headers when exposing the API across origins.
238+
239+
## Release
240+
241+
Build a local binary:
242+
243+
```sh
244+
go build -trimpath -o ./bin/openctrl ./cmd/openctrl
245+
```
246+
247+
Embed a version string:
248+
249+
```sh
250+
go build -trimpath \
251+
-ldflags="-s -w -X main.version=$VERSION" \
252+
-o ./bin/openctrl ./cmd/openctrl
253+
```
254+
255+
Tagged releases are built by GoReleaser for Darwin, FreeBSD, Linux, and
256+
Windows on `amd64` and `arm64`.
257+
258+
## Documentation
259+
260+
- [Master API and runtime contract](docs/master.md)
261+
262+
## License
263+
264+
OpenCtrl is released under the BSD 3-Clause License.

assets/openctrl.png

1.1 MB
Loading

0 commit comments

Comments
 (0)