Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ identities/
*.log
logs/
tmp/
dist/
*.tmp
*.pid
*.out
*.err

# Python tooling caches
.build/
.pytest_cache/
.mypy_cache/
.ruff_cache/
Expand Down
25 changes: 25 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// swift-tools-version: 5.9

import PackageDescription

let package = Package(
name: "OCPDesktop",
platforms: [
.macOS(.v13)
],
products: [
.library(name: "OCPDesktopCore", targets: ["OCPDesktopCore"]),
.executable(name: "OCPDesktop", targets: ["OCPDesktop"])
],
targets: [
.target(name: "OCPDesktopCore"),
.executableTarget(
name: "OCPDesktop",
dependencies: ["OCPDesktopCore"]
),
.testTarget(
name: "OCPDesktopCoreTests",
dependencies: ["OCPDesktopCore"]
)
]
)
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

**A sovereign, local-first compute fabric for trusted devices.**

[![Tests](https://img.shields.io/badge/tests-187%20passing-00FF88?style=flat-square&labelColor=06090F)](./tests/test_sovereign_mesh.py)
[![Release](https://img.shields.io/badge/release-v0.1.4-F6C177?style=flat-square&labelColor=06090F)](./README.md#current-status)
[![Tests](https://img.shields.io/badge/tests-189%20passing-00FF88?style=flat-square&labelColor=06090F)](./tests/test_sovereign_mesh.py)
[![Release](https://img.shields.io/badge/release-v0.1.6-F6C177?style=flat-square&labelColor=06090F)](./README.md#current-status)
[![Version](https://img.shields.io/badge/wire%20version-sovereign--mesh%2Fv1-00D4FF?style=flat-square&labelColor=06090F)](./docs/OCP_STATUS.md)
[![Status](https://img.shields.io/badge/status-active%20development-C8A96E?style=flat-square&labelColor=06090F)](./docs/OCP_MASTER_PLAN.md)
[![Protocol](https://img.shields.io/badge/protocol-OCP%20v0.1-7BC6FF?style=flat-square&labelColor=06090F)](./docs/OCP_STATUS.md)
Expand Down Expand Up @@ -131,7 +131,7 @@ Some devices are powerful. Some are private. Some are fragile. Some are approval
| `server_control_page.py` | Extracted control-deck renderer for the advanced operator surface |
| `server_http_handlers.py` | Grouped HTTP route handlers so `server.py` stays a thin transport host |
| `docs/` | Protocol notes, status, and roadmap |
| `tests/test_sovereign_mesh.py` | Regression suite — 187 tests |
| `tests/test_sovereign_mesh.py` | Regression suite — 189 tests |

**Key runtime concepts:**

Expand Down Expand Up @@ -213,20 +213,37 @@ python3 -m ocp_desktop.launcher

The launcher keeps state under `~/Library/Application Support/OCP/`, can start a local-only node or LAN-reachable Mesh Mode node, opens the OCP app, and shows the phone link for testing on the same Wi-Fi.

Native SwiftPM Mac app:

```bash
swift run OCPDesktop
```

This native Mission Control shell uses the same OCP server, state paths, operator-token phone links, app-status polling, persisted app-history samples, charts, client-derived route topology, guided setup, and default-worker startup behavior as the Python launcher.

Unsigned macOS beta bundle:

```bash
python3 scripts/build_macos_app.py
open dist/OCP.app
```

Unsigned native SwiftPM beta bundle:

```bash
python3 scripts/build_swift_macos_app.py
open "dist/OCP Desktop.app"
```

The beta `.app` requires `python3` to be installed on the Mac. It excludes local state, identities, databases, `.git`, caches, and test artifacts from the bundle.

When Mesh Mode is started from the desktop launcher, copied phone links include an operator token in the URL fragment and the browser stores it locally for OCP POST actions. If you start the server manually with `OCP_HOST=0.0.0.0`, set `OCP_OPERATOR_TOKEN` and open `http://HOST_IP:8421/app#ocp_operator_token=YOUR_TOKEN` from the phone.

Inside the app:

- `Today` shows mesh strength, Autonomic Mesh status, latest proof, next actions, and a phone link/QR
- `Today` shows mesh strength, Autonomic Mesh status, latest proof, proof timeline, next actions, and a phone link/QR
- `Today` can ask the scheduler to choose the best device and can operator-mediate proof-artifact replication without storing remote tokens
- The native Mac app adds Mission Control pages for overview charts, guided setup, route topology, route health, execution readiness, artifact sync, protocol links, and settings
- `Setup` embeds the easy setup flow from `GET /easy`
- `Control` embeds the advanced control deck from `GET /control`
- `Protocol` links the live manifest, device profile, and HTTP contract from `/mesh/*`
Expand All @@ -239,7 +256,7 @@ It now also supports:
- automatic LAN/share URL detection in the easy page so the phone and spare laptop can see the best local address without manual IP hunting
- one-button nearby mesh join with `Connect Everything`
- one-button cooperative verification across the whole current mesh with `Test Whole Mesh`
- an auto-open starter script at `python3 scripts/start_ocp_easy.py`, which now also prints detected LAN URLs when the node is network-reachable
- an auto-open starter script at `python3 scripts/start_ocp_easy.py`, which now also prints detected LAN URLs and advertises default worker readiness for full laptop/workstation nodes

The control module is phone-friendly, so your phone can act as a real operator console for the mesh. From there you can inspect and act on:

Expand Down Expand Up @@ -282,21 +299,26 @@ python3 -m unittest tests.test_sovereign_mesh
python3 server.py --help
```

Current baseline: **187 tests passing.**
Current baseline: **189 tests passing.**

---

## Current Status

**Released in v0.1.4**
**Released in v0.1.6**

- Protocol-first app hardening adds schemas for artifact replication auth, execution readiness, worker capacity, setup timeline events, and app/protocol status.
- Native Mission Control adds a SwiftUI sidebar app with charts and a persisted `/mesh/app/history` API for app-status samples.
- Private proof-artifact replication now supports explicit operator-mediated `remote_auth` and records only redacted audit metadata.
- `/mesh/app/status` now exposes protocol, execution-readiness, artifact-sync, and timeline projections so the app and launcher can explain the mesh without scraping UI state.
- Full laptop/workstation nodes started through the easy script or desktop launcher can auto-advertise a default worker so scheduler demos work out of the box.
- Autonomic Mesh alpha adds route health, one-button activation, proof repair, helper-safe enlistment, and app-visible summaries.
- Desktop Alpha RC adds a Mac beta launcher, unsigned `.app` bundle builder, and a polished `/app` Today surface backed by `GET /mesh/app/status`.
- LAN operator hardening now requires signed peer traffic or operator-token authenticated raw mesh mutations from non-loopback clients.
- Private artifact content fetches now require operator auth unless the artifact policy is public.
- Runtime execution now defaults to explicit environment inheritance, with `inherit_env_allowlist` for deliberate host env pass-through.
- The signed envelope implementation now uses dependency-free Ed25519 helpers under `ed25519-sha512-v1`.
- The protocol-kernel refactor and mission-continuity/treaty foundation from v0.1.3 remain intact, with the full regression suite green at 187 tests.
- The protocol-kernel refactor and mission-continuity/treaty foundation from v0.1.3 remain intact, with the full regression suite green at 189 tests.

**Implemented in the current runtime**

Expand All @@ -319,7 +341,7 @@ Current baseline: **187 tests passing.**
## Current Framing

- `OCP v0.1` — protocol and spec draft
- `v0.1.4` — current implementation release
- `v0.1.6` — current implementation release
- `Sovereign Mesh` — Python-first reference implementation
- `sovereign-mesh/v1` — current wire version

Expand All @@ -342,7 +364,7 @@ OCP is already past "protocol sketch" stage. If it keeps going in this direction
## Related

- [Status](./docs/OCP_STATUS.md)
- [v0.1.4 Release Notes](./docs/RELEASE_v0.1.4.md)
- [v0.1.6 Release Notes](./docs/RELEASE_v0.1.6.md)
- [7026 Vision](./docs/OCP_7026_VISION.md)
- [Quickstart](./docs/QUICKSTART.md)
- [Master Plan](./docs/OCP_MASTER_PLAN.md)
Expand Down
30 changes: 30 additions & 0 deletions Sources/OCPDesktop/App/OCPDesktopApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import SwiftUI

@main
struct OCPDesktopApp: App {
@StateObject private var model = OCPDesktopModel()

var body: some Scene {
WindowGroup {
ContentView(model: model)
.frame(minWidth: 1060, minHeight: 720)
}
.commands {
CommandGroup(replacing: .newItem) {}
CommandMenu("Mesh") {
Button("Activate Mesh") { model.activateMesh() }
.keyboardShortcut("a", modifiers: [.command, .shift])
.disabled(model.isActivating)
Button("Refresh Status") { model.refreshNow() }
.keyboardShortcut("r")
Button("Copy Phone Link") { model.copyPhoneLink() }
.keyboardShortcut("c", modifiers: [.command, .shift])
Divider()
Button("Start Mesh Mode") { model.startMesh() }
Button("Start Local Only") { model.startLocal() }
Button("Stop Server") { model.stop() }
.keyboardShortcut(".", modifiers: [.command])
}
}
}
}
49 changes: 49 additions & 0 deletions Sources/OCPDesktop/Models/DesktopSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Foundation

enum DesktopSection: String, CaseIterable, Identifiable {
case overview
case setup
case routes
case execution
case artifacts
case protocolStatus
case settings

var id: String { rawValue }

var title: String {
switch self {
case .overview: "Overview"
case .setup: "Setup Doctor"
case .routes: "Routes"
case .execution: "Execution"
case .artifacts: "Artifacts"
case .protocolStatus: "Protocol"
case .settings: "Settings"
}
}

var detail: String {
switch self {
case .overview: "Mesh command"
case .setup: "One concrete fix"
case .routes: "Route health"
case .execution: "Worker capacity"
case .artifacts: "Proof sync"
case .protocolStatus: "Contract health"
case .settings: "Node profile"
}
}

var systemImage: String {
switch self {
case .overview: "gauge.with.dots.needle.67percent"
case .setup: "stethoscope"
case .routes: "point.3.connected.trianglepath.dotted"
case .execution: "cpu"
case .artifacts: "shippingbox"
case .protocolStatus: "doc.text.magnifyingglass"
case .settings: "slider.horizontal.3"
}
}
}
53 changes: 53 additions & 0 deletions Sources/OCPDesktop/Services/OCPServerClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation
import OCPDesktopCore

struct OCPServerClient {
var baseURL: String
var operatorToken: String

func fetchStatus() async throws -> AppStatusSnapshot {
try await request(path: "/mesh/app/status", method: "GET", body: nil)
}

func fetchHistory(limit: Int = 240) async throws -> AppStatusHistory {
try await request(path: "/mesh/app/history?limit=\(limit)", method: "GET", body: nil)
}

func recordHistorySample(source: String = "swift-desktop") async throws -> AppHistorySampleResponse {
try await request(path: "/mesh/app/history/sample", method: "POST", body: ["source": source])
}

func activateMesh() async throws {
let _: AppStatusSnapshotEnvelope = try await request(
path: "/mesh/autonomy/activate",
method: "POST",
body: [
"mode": "assisted",
"limit": 24,
"run_proof": true,
"repair": true,
"actor_agent_id": "ocp-swift-desktop"
]
)
}

private func request<T: Decodable>(path: String, method: String, body: [String: Any]?) async throws -> T {
guard let url = URL(string: baseURL + path) else { throw URLError(.badURL) }
var request = URLRequest(url: url)
request.httpMethod = method
if !operatorToken.isEmpty {
request.setValue(operatorToken, forHTTPHeaderField: "X-OCP-Operator-Token")
}
if let body {
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: body)
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
throw URLError(.badServerResponse)
}
return try JSONDecoder().decode(T.self, from: data)
}
}

private struct AppStatusSnapshotEnvelope: Decodable {}
17 changes: 17 additions & 0 deletions Sources/OCPDesktop/Services/RepoLocator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

enum RepoLocator {
static func defaultRepoRoot() -> URL {
let env = ProcessInfo.processInfo.environment["OCP_REPO_ROOT"] ?? ""
if !env.isEmpty {
return URL(fileURLWithPath: env, isDirectory: true)
}
if let resourceURL = Bundle.main.resourceURL {
let bundled = resourceURL.appendingPathComponent("open-compute-protocol", isDirectory: true)
if FileManager.default.fileExists(atPath: bundled.appendingPathComponent("server.py").path) {
return bundled
}
}
return URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
}
}
Loading
Loading