Skip to content

Commit 741bbe1

Browse files
committed
feat: Add Borg Console and release workflow
1 parent b3755da commit 741bbe1

6 files changed

Lines changed: 687 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v6
19+
with:
20+
go-version-file: 'go.mod'
21+
22+
- name: Get version
23+
id: version
24+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
25+
26+
- name: Build binaries
27+
run: |
28+
mkdir -p dist
29+
30+
# Linux amd64
31+
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o dist/borg-linux-amd64 main.go
32+
33+
# Linux arm64
34+
GOOS=linux GOARCH=arm64 go build -ldflags "-s -w" -o dist/borg-linux-arm64 main.go
35+
36+
# macOS amd64
37+
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o dist/borg-darwin-amd64 main.go
38+
39+
# macOS arm64
40+
GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o dist/borg-darwin-arm64 main.go
41+
42+
# Windows amd64
43+
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o dist/borg-windows-amd64.exe main.go
44+
45+
- name: Build WASM module
46+
run: |
47+
GOOS=js GOARCH=wasm go build -o dist/stmf.wasm ./pkg/wasm/stmf/
48+
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" dist/ 2>/dev/null || \
49+
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" dist/
50+
51+
- name: Build Console STIM
52+
run: |
53+
# Build borg for current platform first
54+
go build -o borg main.go
55+
56+
# Build the encrypted console demo
57+
./borg console build -p "borg-demo" -o dist/console.stim -s js/borg-stmf
58+
59+
- name: Create checksums
60+
run: |
61+
cd dist
62+
sha256sum * > checksums.txt
63+
64+
- name: Create Release
65+
uses: softprops/action-gh-release@v1
66+
with:
67+
name: Borg ${{ steps.version.outputs.VERSION }}
68+
body: |
69+
## Borg ${{ steps.version.outputs.VERSION }}
70+
71+
### Downloads
72+
73+
| Platform | Binary |
74+
|----------|--------|
75+
| Linux x64 | `borg-linux-amd64` |
76+
| Linux ARM64 | `borg-linux-arm64` |
77+
| macOS x64 | `borg-darwin-amd64` |
78+
| macOS ARM64 | `borg-darwin-arm64` |
79+
| Windows x64 | `borg-windows-amd64.exe` |
80+
81+
### Console Demo
82+
83+
The `console.stim` is an encrypted PWA demo. Run it with:
84+
```bash
85+
borg console serve console.stim --open
86+
```
87+
Password: `borg-demo`
88+
89+
### WASM Module
90+
91+
- `stmf.wasm` - Browser encryption module
92+
- `wasm_exec.js` - Go WASM runtime
93+
94+
files: |
95+
dist/borg-linux-amd64
96+
dist/borg-linux-arm64
97+
dist/borg-darwin-amd64
98+
dist/borg-darwin-arm64
99+
dist/borg-windows-amd64.exe
100+
dist/stmf.wasm
101+
dist/wasm_exec.js
102+
dist/console.stim
103+
dist/checksums.txt
104+
draft: false
105+
prerelease: false

cmd/console.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/Snider/Borg/pkg/console"
9+
"github.com/Snider/Borg/pkg/tim"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var consoleCmd = NewConsoleCmd()
14+
15+
// NewConsoleCmd creates the console parent command.
16+
func NewConsoleCmd() *cobra.Command {
17+
cmd := &cobra.Command{
18+
Use: "console",
19+
Short: "Manage encrypted PWA console demos",
20+
Long: `The Borg Console packages and serves encrypted PWA demos.
21+
22+
Build a console STIM:
23+
borg console build -p "password" -o console.stim
24+
25+
Serve with unlock page:
26+
borg console serve console.stim --open
27+
28+
Serve pre-unlocked:
29+
borg console serve console.stim -p "password" --open`,
30+
}
31+
32+
cmd.AddCommand(NewConsoleBuildCmd())
33+
cmd.AddCommand(NewConsoleServeCmd())
34+
35+
return cmd
36+
}
37+
38+
// NewConsoleBuildCmd creates the build subcommand.
39+
func NewConsoleBuildCmd() *cobra.Command {
40+
cmd := &cobra.Command{
41+
Use: "build",
42+
Short: "Build a console STIM from demo files",
43+
Long: `Packages HTML demo files into an encrypted STIM container.
44+
45+
By default, looks for files in js/borg-stmf/ directory.
46+
Required files: index.html, support-reply.html, stmf.wasm, wasm_exec.js`,
47+
RunE: func(cmd *cobra.Command, args []string) error {
48+
password, _ := cmd.Flags().GetString("password")
49+
output, _ := cmd.Flags().GetString("output")
50+
sourceDir, _ := cmd.Flags().GetString("source")
51+
52+
if password == "" {
53+
return fmt.Errorf("password is required")
54+
}
55+
56+
// Create new TIM
57+
m, err := tim.New()
58+
if err != nil {
59+
return fmt.Errorf("creating TIM: %w", err)
60+
}
61+
62+
// Required demo files
63+
files := []string{
64+
"index.html",
65+
"support-reply.html",
66+
"stmf.wasm",
67+
"wasm_exec.js",
68+
}
69+
70+
// Add each file to the TIM
71+
for _, f := range files {
72+
path := filepath.Join(sourceDir, f)
73+
data, err := os.ReadFile(path)
74+
if err != nil {
75+
return fmt.Errorf("reading %s: %w", f, err)
76+
}
77+
m.RootFS.AddData(f, data)
78+
fmt.Printf(" + %s (%d bytes)\n", f, len(data))
79+
}
80+
81+
// Encrypt to STIM
82+
stim, err := m.ToSigil(password)
83+
if err != nil {
84+
return fmt.Errorf("encrypting STIM: %w", err)
85+
}
86+
87+
// Write output
88+
if err := os.WriteFile(output, stim, 0644); err != nil {
89+
return fmt.Errorf("writing output: %w", err)
90+
}
91+
92+
fmt.Printf("\nBuilt: %s (%d bytes)\n", output, len(stim))
93+
fmt.Println("Encrypted with ChaCha20-Poly1305")
94+
95+
return nil
96+
},
97+
}
98+
99+
cmd.Flags().StringP("password", "p", "", "Encryption password (required)")
100+
cmd.Flags().StringP("output", "o", "console.stim", "Output file")
101+
cmd.Flags().StringP("source", "s", "js/borg-stmf", "Source directory")
102+
cmd.MarkFlagRequired("password")
103+
104+
return cmd
105+
}
106+
107+
// NewConsoleServeCmd creates the serve subcommand.
108+
func NewConsoleServeCmd() *cobra.Command {
109+
cmd := &cobra.Command{
110+
Use: "serve [stim-file]",
111+
Short: "Serve an encrypted console STIM",
112+
Long: `Starts an HTTP server to serve encrypted STIM content.
113+
114+
Without a password, shows a dark-themed unlock page.
115+
With a password, decrypts immediately and serves content.
116+
117+
Examples:
118+
borg console serve demos.stim --open
119+
borg console serve demos.stim -p "password" --port 3000`,
120+
Args: cobra.ExactArgs(1),
121+
RunE: func(cmd *cobra.Command, args []string) error {
122+
stimPath := args[0]
123+
password, _ := cmd.Flags().GetString("password")
124+
port, _ := cmd.Flags().GetString("port")
125+
openBrowser, _ := cmd.Flags().GetBool("open")
126+
127+
// Create server
128+
server, err := console.NewServer(stimPath, password, port)
129+
if err != nil {
130+
return err
131+
}
132+
133+
// Print status
134+
fmt.Printf("Borg Console serving at %s\n", server.URL())
135+
if password != "" {
136+
fmt.Println("Status: Unlocked (password provided)")
137+
} else {
138+
fmt.Println("Status: Locked (unlock page active)")
139+
}
140+
fmt.Println()
141+
142+
// Open browser if requested
143+
if openBrowser {
144+
if err := console.OpenBrowser(server.URL()); err != nil {
145+
fmt.Printf("Warning: could not open browser: %v\n", err)
146+
}
147+
}
148+
149+
// Start serving
150+
return server.Start()
151+
},
152+
}
153+
154+
cmd.Flags().StringP("password", "p", "", "Decryption password (skip unlock page)")
155+
cmd.Flags().String("port", "8080", "Port to serve on")
156+
cmd.Flags().Bool("open", false, "Auto-open browser")
157+
158+
return cmd
159+
}
160+
161+
func init() {
162+
RootCmd.AddCommand(consoleCmd)
163+
}

console.stim

5.11 MB
Binary file not shown.

pkg/console/browser.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Package console provides an encrypted PWA demo server with browser integration.
2+
package console
3+
4+
import (
5+
"fmt"
6+
"os/exec"
7+
"runtime"
8+
)
9+
10+
// OpenBrowser opens the default browser to the specified URL.
11+
// Supports macOS, Linux, and Windows.
12+
func OpenBrowser(url string) error {
13+
var cmd *exec.Cmd
14+
15+
switch runtime.GOOS {
16+
case "darwin":
17+
cmd = exec.Command("open", url)
18+
case "linux":
19+
cmd = exec.Command("xdg-open", url)
20+
case "windows":
21+
cmd = exec.Command("cmd", "/c", "start", url)
22+
default:
23+
return fmt.Errorf("unsupported platform: %s", runtime.GOOS)
24+
}
25+
26+
return cmd.Start()
27+
}

0 commit comments

Comments
 (0)