Skip to content

Commit 84c2e92

Browse files
committed
feat: High-Rigor HMAC security layer and unified panel manifests
1 parent 37e22fd commit 84c2e92

16 files changed

Lines changed: 719 additions & 699 deletions

File tree

.github/workflows/jekyll.yml

Lines changed: 0 additions & 66 deletions
This file was deleted.

adapter/v/src/main.v

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,17 @@ fn (h RestHandler) handle(req http.Request) http.Response {
866866
}
867867
return handle_order_ticket(h.app, req.data)
868868
}
869+
if path == '/cartridges/ssg-mcp/webhook' {
870+
if req.method != .post {
871+
return error_response(405, 'POST required for webhooks')
872+
}
873+
// High-Rigor HMAC Security Check
874+
signature := req.header.get(.x_hub_signature_256) or { '' }
875+
if !verify_github_signature(req.data, signature) {
876+
return error_response(401, 'unauthorized: invalid signature')
877+
}
878+
return handle_cartridge_invoke(h.app, 'ssg-mcp', req.data)
879+
}
869880
// GET /cartridges — list all cartridges
870881
if path == '/cartridges' {
871882
return json_response(json.encode(h.app.build_matrix()))

adapter/v/src/webhook.v

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
//
4+
// BoJ Server — Webhook Security Module
5+
//
6+
// Implements HMAC-SHA256 verification for GitHub and other forge webhooks.
7+
// Ensures that incoming build triggers are authentic and untampered.
8+
9+
module main
10+
11+
import crypto.hmac
12+
import crypto.sha256
13+
import os
14+
15+
// verify_github_signature validates the X-Hub-Signature-256 header.
16+
// Secret is sourced from BOJ_GITHUB_WEBHOOK_SECRET env var.
17+
fn verify_github_signature(payload string, signature_header string) bool {
18+
secret := os.getenv('BOJ_GITHUB_WEBHOOK_SECRET')
19+
if secret == '' {
20+
// If no secret is configured, we fail closed for security.
21+
return false
22+
}
23+
24+
if !signature_header.starts_with('sha256=') {
25+
return false
26+
}
27+
28+
// Extract the hex signature (strip 'sha256=')
29+
expected_sig := signature_header[7..]
30+
31+
// Calculate HMAC-SHA256 of the payload
32+
actual_sig := hmac.new(secret.bytes(), payload.bytes(), sha256.sum, sha256.block_size).hex()
33+
34+
// Constant-time comparison to prevent timing attacks
35+
return actual_sig == expected_sig
36+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"$schema": "panll-harness/v1",
3+
"service_id": "rpa-elysium",
4+
"service_name": "RPA Elysium Filesystem Automation",
5+
"version": "0.1.0",
6+
"protocol": "http",
7+
"default_endpoint": "http://localhost:7800/api/v1",
8+
"health_check": {
9+
"path": "/health",
10+
"interval_ms": 30000,
11+
"timeout_ms": 5000,
12+
"healthy_threshold": 1,
13+
"unhealthy_threshold": 3
14+
},
15+
"data_sources": {
16+
"rpa-elysium://workflow/status": {
17+
"path": "/workflow/status",
18+
"method": "GET",
19+
"returns": "WorkflowStatus",
20+
"description": "Current workflow execution status (idle/running/paused/stopped/error)"
21+
},
22+
"rpa-elysium://workflow/metrics/events_processed": {
23+
"path": "/workflow/metrics",
24+
"method": "GET",
25+
"returns": "u64",
26+
"jq_extract": ".events_processed",
27+
"description": "Total events processed by the workflow engine"
28+
},
29+
"rpa-elysium://workflow/metrics/actions_executed": {
30+
"path": "/workflow/metrics",
31+
"method": "GET",
32+
"returns": "u64",
33+
"jq_extract": ".actions_executed",
34+
"description": "Total actions executed"
35+
},
36+
"rpa-elysium://workflow/metrics/error_count": {
37+
"path": "/workflow/metrics",
38+
"method": "GET",
39+
"returns": "u64",
40+
"jq_extract": ".error_count",
41+
"description": "Total errors encountered"
42+
},
43+
"rpa-elysium://workflow/watch_paths": {
44+
"path": "/workflow/watch_paths",
45+
"method": "GET",
46+
"returns": "[WatchPath]",
47+
"description": "List of watched directories with event counts"
48+
},
49+
"rpa-elysium://workflow/rules": {
50+
"path": "/workflow/rules",
51+
"method": "GET",
52+
"returns": "[Rule]",
53+
"description": "Active rules with match counts"
54+
},
55+
"rpa-elysium://workflow/events/timeline": {
56+
"path": "/workflow/events?format=timeline",
57+
"method": "GET",
58+
"returns": "[TimelineEvent]",
59+
"description": "Event timeline for the last hour"
60+
},
61+
"rpa-elysium://workflow/actions/recent": {
62+
"path": "/workflow/actions?limit=25",
63+
"method": "GET",
64+
"returns": "[ActionResult]",
65+
"description": "Recent action execution results"
66+
},
67+
"rpa-elysium://workflow/fsm/state": {
68+
"path": "/workflow/fsm",
69+
"method": "GET",
70+
"returns": "FSMState",
71+
"description": "proven-fsm workflow state machine current state"
72+
},
73+
"rpa-elysium://queue/state": {
74+
"path": "/queue/state",
75+
"method": "GET",
76+
"returns": "QueueState",
77+
"description": "proven-queueconn subscription state (for HAR integration)"
78+
},
79+
"rpa-elysium://plugins/count": {
80+
"path": "/plugins/count",
81+
"method": "GET",
82+
"returns": "u64",
83+
"description": "Number of loaded WASM plugins"
84+
},
85+
"rpa-elysium://plugins/list": {
86+
"path": "/plugins",
87+
"method": "GET",
88+
"returns": "[PluginInfo]",
89+
"description": "List of registered plugins with metadata"
90+
},
91+
"rpa-elysium://plugins/sandbox/memory_usage": {
92+
"path": "/plugins/sandbox/memory",
93+
"method": "GET",
94+
"returns": "u64",
95+
"description": "Total sandbox memory usage in bytes"
96+
},
97+
"rpa-elysium://plugins/logs/recent": {
98+
"path": "/plugins/logs?limit=20",
99+
"method": "GET",
100+
"returns": "[PluginLog]",
101+
"description": "Recent plugin execution log entries"
102+
}
103+
},
104+
"panels": [
105+
"panels/fs-workflow/panel.json",
106+
"panels/plugin-status/panel.json"
107+
],
108+
"capabilities": ["filesystem", "scheduling", "plugin"],
109+
"clade": "automation/filesystem"
110+
}

cartridges/browser-mcp/panels/manifest.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77
"license": "PMPL-1.0-or-later",
88
"author": "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
99
"data_sources": [
10+
{
11+
"id": "node_health",
12+
"label": "BoJ Node Health",
13+
"type": "boolean",
14+
"endpoint": "/health",
15+
"poll_interval_ms": 60000
16+
},
17+
{
18+
"id": "node_uptime",
19+
"label": "Node Uptime",
20+
"type": "integer",
21+
"endpoint": "/health",
22+
"poll_interval_ms": 60000
23+
},
1024
{
1125
"id": "connection_status",
1226
"label": "Connection Status",
@@ -45,6 +59,25 @@
4559
}
4660
],
4761
"widgets": [
62+
{
63+
"id": "node_health_status",
64+
"type": "status-badge",
65+
"title": "BoJ Node",
66+
"data_source": "node_health",
67+
"style": {
68+
"true": "success",
69+
"false": "danger"
70+
},
71+
"position": { "row": 0, "col": 0, "width": 1, "height": 1 }
72+
},
73+
{
74+
"id": "node_uptime_display",
75+
"type": "numeric-display",
76+
"title": "Node Uptime",
77+
"data_source": "node_uptime",
78+
"icon": "timer",
79+
"position": { "row": 0, "col": 1, "width": 1, "height": 1 }
80+
},
4881
{
4982
"id": "status_indicator",
5083
"type": "status-badge",

0 commit comments

Comments
 (0)