Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
f43e834
chore: add implementation task briefs and finalized design docs (phas…
May 31, 2026
130a974
feat: add Workstation flag and workstation-only middleware to ServerC…
May 31, 2026
43c5ac1
feat: make dev identity configurable, default to OS user (W2)
May 31, 2026
1a98000
chore: relabel dev token as "developer token" in UI and docs (W3)
May 31, 2026
0c60f3a
feat: add /system/* API endpoints for workstation onboarding (W1/W2)
May 31, 2026
9cef05e
feat: add /onboarding wizard shell with steps 0-3 and done step (W1)
May 31, 2026
d67b84c
feat: auto-open browser and print onboarding URL on server start (D8)
May 31, 2026
9d7c257
feat: add Go-native harness image pull with per-image SSE progress (W4)
May 31, 2026
54ddcfd
feat: add local image build option via POST /system/images/build (W4)
May 31, 2026
646d7e3
feat: wire wizard images step to pull/build endpoints (W4)
May 31, 2026
e747707
feat: add path-safety helpers for workstation filesystem operations (W5)
May 31, 2026
d679fd8
feat: add fenced fs/list, fs/mkdir, fs/validate-path endpoints (W5)
May 31, 2026
707c20a
feat: add linked-grove creation mode with directory browser to projec…
May 31, 2026
4bc13fc
feat: wire wizard workspace step to linked-grove and hub-native creat…
May 31, 2026
d10c922
test: add tests for workstation onboarding API and security fencing
May 31, 2026
b3f5239
docs: update README with workstation quick start and brew install
May 31, 2026
dfd4611
chore: add round-1 review fix brief
May 31, 2026
148b830
fix: correct home-directory boundary check in fs/list and fs/mkdir (M1)
May 31, 2026
a714a65
fix: write runtime to active profile runtime field not active_profile…
May 31, 2026
0856239
fix: document intentional unfenced validate-path + assertLoopback (M3)
May 31, 2026
ca409e1
fix: address minor review findings (m1-m5)
May 31, 2026
570ce07
chore: add round-2 review fix brief
May 31, 2026
f456df3
fix: use server-lifetime context in image pull/build goroutines (m4)
May 31, 2026
e37025a
fix: remove devUser self-shadow in auth.go (m5) and fix empty-ActiveP…
May 31, 2026
472e24d
chore: add ci-full fix task brief
May 31, 2026
948010a
chore: add PR #264 feedback fix brief
May 31, 2026
07eaaf3
fix: handle top-level pull error events to prevent infinite spinner (H1)
May 31, 2026
097d4fb
fix: normalize Windows path separators and drive letter breadcrumbs i…
May 31, 2026
e326671
fix: check scanner.Err after build log scan loop (M2)
May 31, 2026
3765e4c
fix: check context cancellation in image pull loop (M3)
May 31, 2026
4ee05ff
fix: add concurrency guard for concurrent image build requests (M4)
May 31, 2026
c709b1d
fix: apply gofmt formatting to all Go source files
May 31, 2026
46ee5e8
fix: address golangci-lint findings in hub package
May 31, 2026
8e137a7
fix: add SCION_PROJECT_ID to env var cleanup in tests and use ephemer…
May 31, 2026
a993028
fix: resolve post-rebase build errors (unused storage import, product…
Jun 1, 2026
552aad8
fix: suppress dev-auth warning in workstation mode (#72)
Jun 1, 2026
00b1f6e
chore: add phantom daemon fix task brief
Jun 2, 2026
0c4a03a
chore: add onboarding bugs 3-4-5 fix brief
Jun 2, 2026
1d0365d
feat: detect port conflicts on server start to catch phantom daemons
scion Jun 2, 2026
9a77b0a
feat: add scion server stop --force to kill phantom processes by port
scion Jun 2, 2026
f97ef63
fix: only resume wizard progress if user has previously started onboa…
scion Jun 2, 2026
c72104c
fix: display full registry-qualified image names in wizard step 5 (bu…
scion Jun 2, 2026
59b4be2
fix: add registry and buildAvailable to system/status; hide build opt…
scion Jun 2, 2026
d0a09a4
fix: store imageRegistry as component state; use it in initial and qu…
Jun 3, 2026
daf2a3a
fix: scope git version check to worktree-using commands only (scion s…
Jun 5, 2026
8a72341
fix: capture needsOnboarding before daemon start to prevent /onboardi…
Jun 5, 2026
4465fb9
fix: fail fast on empty image_registry in pull handler; show registry…
Jun 5, 2026
26dc399
fix: identity persists immediately after onboarding step 0 (bug 10); …
Jun 5, 2026
585a8c6
design: add Integrations Admin design doc
ptone Jun 6, 2026
48b3c22
design: reframe integrations architecture for multi-topology support
ptone Jun 7, 2026
d58cee7
design: add 4 canonical deployment modes and Cloud Solo topology
ptone Jun 7, 2026
7eed70a
design: fix Cloud Solo — workstation in Cloud Run, uses SQLite
ptone Jun 7, 2026
afa00f8
design: Cloud Solo uses polling — IAP blocks inbound webhooks
ptone Jun 7, 2026
3695320
design: add lifecycle journey, onboarding alignment, phased workstreams
ptone Jun 7, 2026
971db10
design: restructure around lifecycle journey, admin UI as one facet
ptone Jun 7, 2026
19ef8a2
feat: add AddSpoke() to FanOutEventBus for dynamic spoke addition
ptone Jun 7, 2026
f05e7e3
feat: telegram setup service with hot-start and API endpoints
ptone Jun 7, 2026
6ff026c
feat: add Telegram onboarding step to wizard (step 6)
ptone Jun 7, 2026
e19ab35
fix: AddSpoke replaces by name to prevent dead spoke duplicates
ptone Jun 7, 2026
a5bc7e6
fix: hot-start before persist, guard proxy-nil race
ptone Jun 7, 2026
1f30dca
fix: inbound_mode "polling" → "poll" to match plugin contract
ptone Jun 8, 2026
f80f88d
fix: launch telegram plugin with SCION_TELEGRAM_V2=1
ptone Jun 8, 2026
9c81e0c
fix: start polling in Configure() when hub creds are present
ptone Jun 8, 2026
f1b6dc8
fix: /register uses plain text for localhost URLs, fail-safe fallback
ptone Jun 8, 2026
188b670
fix: persist onboarding wizard step to localStorage for durable resume
ptone Jun 8, 2026
78d21d1
fix: resolve image-pull step hang on podman when images already exist
ptone Jun 8, 2026
f3c04ad
fix: include podman in macOS runtime auto-detection
ptone Jun 8, 2026
b348565
fix: use runtime probing for embedded defaults instead of OS-only check
ptone Jun 8, 2026
670f003
fix: align factory.go auto-detection priority with DetectLocalRuntime
ptone Jun 8, 2026
2627e5e
refactor: factory.go calls config.DetectLocalRuntime for single sourc…
ptone Jun 8, 2026
a236371
fix: default image_registry in onboarding wizard for accept-and-conti…
ptone Jun 8, 2026
9bc12b2
feat: add RemoveSpoke and StopPlugin for integration disable
ptone Jun 8, 2026
edc5979
feat: admin integration endpoints (list, enable, disable)
ptone Jun 8, 2026
1ce00da
feat: admin integrations page with multi-integration shell
ptone Jun 8, 2026
65289ac
fix: resolve workspace mount to project dir instead of config parent …
ptone Jun 8, 2026
36fec7b
fix: onboarding resume step initialization and cleanup
ptone Jun 8, 2026
b9f036c
fix: add per-image timeout to image pull goroutine
ptone Jun 8, 2026
3f253de
test: verify externalized local-dir project mounts workspace correctly
ptone Jun 8, 2026
b833685
fix: memoize DetectLocalRuntime and align detect-failure fallback
ptone Jun 8, 2026
b1cd6b6
fix: auto-select newly created folder in directory picker
ptone Jun 8, 2026
7c39fcb
fix: backend review findings — rollback, leaks, Close, env unify
ptone Jun 8, 2026
bce2f76
fix: EnsureTelegramEnv — restart-safe v2 without migration
ptone Jun 8, 2026
5f3441e
fix: post-rebase test compilation against main's API drift
ptone Jun 8, 2026
eaa0161
fix: gofmt formatting for CI compliance
ptone Jun 8, 2026
6671872
fix: add !no_sqlite build tag to signing_key_shared_test.go
ptone Jun 8, 2026
573ca97
fix: update printWorkstationQuickstart test calls to match signature
ptone Jun 8, 2026
8f121a1
fix: errcheck and lint issues for CI compliance
ptone Jun 8, 2026
8fb003f
fix: remaining errcheck lint issues in our files
ptone Jun 8, 2026
beab262
fix: gofmt pkg/hub/server.go (Format Check)
ptone Jun 10, 2026
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
468 changes: 468 additions & 0 deletions .design/integrations-admin.md

Large diffs are not rendered by default.

447 changes: 447 additions & 0 deletions .design/linked-groves-ui.md

Large diffs are not rendered by default.

633 changes: 633 additions & 0 deletions .design/workstation-onboarding-wizard.md

Large diffs are not rendered by default.

343 changes: 343 additions & 0 deletions .design/workstation-onboarding.md

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions .tasks/ci-full-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Run ci-full and Fix All Issues

**Branch:** workstation-improvements
**Goal:** Run `make ci-full` and fix every failure, including pre-existing ones unrelated to our changes.

---

## Steps

1. Run `make ci-full` and capture all failures
2. Fix each failure category in turn:
- `fmt-check` failures → run `make fmt` then re-check
- `web` (Vite build) failures → fix TypeScript/JS build errors
- `web-typecheck` failures → fix TypeScript type errors
- `lint` failures → fix lint errors (go vet, staticcheck, etc.)
- `golangci-lint` failures → fix golangci-lint findings
- `test-fast` failures → fix failing tests (including pre-existing ones)
- `build` failures → fix compile errors

3. After fixing each category, re-run that specific step to confirm it passes before moving on
4. Run full `make ci-full` at the end to confirm everything passes together

## Known pre-existing test failures (from earlier run)

These tests in `pkg/config` were failing before our changes — fix them too:
- `TestIsInsideProject`
- `TestRequireProjectPath_NoProjectError`
- `TestFindProjectRoot_HubContextNoScion_Disabled`
- `TestDiscoverProjects_GitProjectWithExternalConfigUsesWorkspaceMarkerProjectID`
- `TestIsHubContext`

Investigate each failure message and fix the root cause.

## Commit instructions

- Commit fixes in logical groups (e.g. one commit for fmt fixes, one for test fixes, etc.)
- Use clear commit messages describing what was broken and what was fixed
- Run `make ci-full` one final time to confirm all green before the last commit
- Do not open PRs — commit directly to `workstation-improvements`
111 changes: 111 additions & 0 deletions .tasks/onboarding-bugs-3-4-5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Fix: Onboarding Bugs 3, 4, 5

**Branch:** workstation-improvements
**Commit all changes to the current branch.**

---

## Bug 3 — Wizard skips to step 4 on fresh install

**File:** `web/src/components/pages/onboarding.ts`

**Problem:** `initialize()` auto-advances `currentStep` based on backend status flags (`runtimeOK`, `harnessesSeeded`). After `scion init --machine`, both are already true, so the wizard jumps to step 4 (Images) on first launch, skipping Identity, System Check, Runtime, and Harness steps.

**Fix:** The resume logic must only fire if the user has previously progressed through the wizard. Add a `previouslyStarted` check — use `sessionStorage.getItem('onboardingStarted')` (set it when the user clicks "Next" for the first time). Only run the resume auto-advance if `previouslyStarted` is true:

```typescript
async initialize() {
const status = await this.fetchStatus();
const previouslyStarted = sessionStorage.getItem('onboardingStarted') === 'true';

if (previouslyStarted) {
// Resume logic: advance past already-complete steps
if (status.identitySet && this.currentStep === 0) this.currentStep = 1;
if (status.runtimeOK && this.currentStep <= 2) this.currentStep = Math.max(this.currentStep, 3);
if (status.harnessesSeeded && this.currentStep <= 3) this.currentStep = Math.max(this.currentStep, 4);
}
// Always start at step 0 on fresh install regardless of backend status
}
```

Set `sessionStorage.setItem('onboardingStarted', 'true')` when the user advances from step 0 for the first time (i.e. in the "Next" handler for step 0, or on any step advance).

Clear `sessionStorage.removeItem('onboardingStarted')` when the wizard completes (step "Done").

---

## Bug 4 — Image names missing registry prefix on step 5

**Files:** `web/src/components/pages/onboarding.ts` and `pkg/hub/system_handlers.go`

### Frontend fix

**Problem:** The event handler extracts only the harness name via `imageNameToHarness()` and reconstructs a partial name for display. The full registry-qualified image name is in the SSE event as `d['image']` but is discarded.

**Fix:** Store and display the full image name from the event data. In the image status map, use the full image name as the key (not the harness name), OR store both:

```typescript
// In the SSE event handler (around line 1004-1016):
if (d['image']) {
const fullImageName = d['image'] as string; // e.g. "ghcr.io/homebrew-scion/scion-claude:latest"
const status = d['status'] as string;
// Store by full image name
const next = new Map(this.imageStatuses);
next.set(fullImageName, { status, error: d['error'] as string | undefined });
this.imageStatuses = next;
}
```

In the render template (around line 872), display the full image name:
```typescript
// Show: "ghcr.io/homebrew-scion/scion-claude:latest" instead of "scion-claude:latest"
${[...this.imageStatuses.entries()].map(([image, info]) => html`
<div class="image-status">
<code>${image}</code>
<span class="status-${info.status}">${info.status}</span>
${info.error ? html`<span class="error">${info.error}</span>` : ''}
</div>
`)}
```

### Backend: add registry to /system/status

**Problem:** The frontend has no way to verify what registry is configured.

**Fix:** Add `ImageRegistry string` to `OnboardingStatus` in `pkg/hub/system_handlers.go` and populate it by reading `image_registry` from settings (via `config.LoadSettings("")` → `settings.ImageRegistry` or however it's stored). The frontend can display this in the images step header: "Pulling from: ghcr.io/homebrew-scion".

---

## Bug 5 — "Build locally" fails for Homebrew installs

**File:** `pkg/hub/system_handlers.go` (handleSystemImagesBuild, ~line 492-504)

**Problem:** Build script is not present in Homebrew installations. The handler falls back to a CWD/binary-relative path that doesn't exist.

**Fix:** Add an availability check before the build option is shown or invoked:

In `handleSystemImagesBuild`:
```go
// Check build script exists before attempting
buildScript := resolveBuildScript() // existing resolution logic
if buildScript == "" {
http.Error(w, `{"error":"local builds require a source checkout; use image pull instead","buildUnavailable":true}`, http.StatusUnprocessableEntity)
return
}
```

In `GET /system/status`, add `BuildAvailable bool` that returns `true` only if the build script can be resolved. Frontend uses this to:
- Hide the "Build locally" button when `!status.buildAvailable`
- Show an explanatory note: "Pre-built images are available from ghcr.io/homebrew-scion. Local builds require a source checkout."

This ensures that for Homebrew installs the user only sees the pull path, while developer checkouts still get the build option.

---

## Commit Instructions

- `fix: only resume wizard progress if user has previously started onboarding (bug 3)`
- `fix: display full registry-qualified image names in wizard step 5 (bug 4)`
- `fix: add registry and buildAvailable to system/status; hide build option for brew installs (bug 5)`
- Run `go build ./...` and `go vet ./...` before committing Go changes
- Do not open PRs — commit directly to `workstation-improvements`
55 changes: 55 additions & 0 deletions .tasks/phantom-daemon-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Fix: Phantom Daemon After Reinstall

**Branch:** workstation-improvements
**Key files:** `pkg/daemon/daemon.go`, `cmd/server_daemon.go`
**Commit all changes to the current branch.**

## Fix 1 — Port conflict detection in `scion server start`

In `cmd/server_daemon.go` `runServerStartOrDaemon()` (around line 34), after the existing `StatusComponent()` check, add a port probe:

```go
// Check for phantom processes holding server ports even without a PID file
if phantomPorts := detectOccupiedPorts(cfg); len(phantomPorts) > 0 {
fmt.Fprintf(os.Stderr, "Error: the following ports are already in use: %v\n", phantomPorts)
fmt.Fprintf(os.Stderr, "A previous server process may be running without a PID file.\n")
fmt.Fprintf(os.Stderr, "Run 'scion server stop --force' to kill any process on these ports.\n")
return fmt.Errorf("port conflict: ports %v are occupied", phantomPorts)
}
```

Implement `detectOccupiedPorts(cfg)` in `pkg/daemon/daemon.go` or a new `pkg/daemon/ports.go`:
- Try to bind (and immediately release) each server port (default: 8080 for web/hub, 9800 for broker, 9810 for broker gRPC or whatever ports the config uses)
- If binding fails → port is occupied
- Return the list of occupied ports

Look at how the server resolves its ports from `cfg` (search for `cfg.Hub.Port`, `cfg.RuntimeBroker.Port` etc.) to know which ports to probe.

Use `net.Listen("tcp", fmt.Sprintf(":%d", port))` — if it succeeds, close it immediately and mark as free; if it fails with EADDRINUSE, mark as occupied.

## Fix 2 — `scion server stop --force`

In `cmd/server_daemon.go` `runServerStop()` (around line 165), add a `--force` flag:

```
--force Kill any process listening on the server ports, even without a PID file
```

When `--force` is set:
1. Probe the server ports (reuse `detectOccupiedPorts`)
2. For each occupied port, find the PID of the process holding it using `lsof -ti :<port>` (macOS/Linux) or `ss -tlnp` (Linux fallback)
3. Kill the PID with SIGTERM, wait up to 3s, then SIGKILL if still running
4. Report what was killed

Simple implementation using `exec.Command("lsof", "-ti", fmt.Sprintf(":%d", port))` — parse the output as a PID, then `syscall.Kill(pid, syscall.SIGTERM)`.

If no PID file and no occupied ports, print "No running server found."

Add `--force` to the `stop` command's flags in `cmd/server_daemon.go`.

## Commit Instructions

- `feat: detect port conflicts on server start to catch phantom daemons`
- `feat: add scion server stop --force to kill phantom processes by port`
- Run `go build ./...` and `go vet ./...` before committing
- Do not open PRs — commit directly to `workstation-improvements`
66 changes: 66 additions & 0 deletions .tasks/phase-0-1-foundations-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Phase 0+1: Foundations, Identity & Labels

**Branch:** workstation-improvements
**Design docs:** `.design/workstation-onboarding.md` §7, `.design/workstation-onboarding-wizard.md`, `.design/linked-groves-ui.md`
**Commit all changes to the current branch as you go.**

---

## Phase 0 — Foundations

### 0.1 — `Workstation bool` on `hub.ServerConfig`

File: `pkg/hub/web.go` (or wherever `ServerConfig` is defined — search for the struct)

- Add `Workstation bool` field to `hub.ServerConfig`
- In `cmd/server_foreground.go` around line 774, set `cfg.Workstation = !productionMode` when building the server config
- On the server struct, store it: `s.workstation bool`
- Add two helpers:
- `requireWorkstation(next http.Handler) http.Handler` — middleware that returns 404 if `!s.workstation`
- `assertLoopback(r *http.Request) error` — checks `r.RemoteAddr` is loopback (127.x or ::1)
- These will gate all `/system/*` and filesystem endpoints

### 0.2 — `GetEmbeddedBrokerID()` accessor

- Add a method on the server (or hub config) that returns the pre-generated embedded broker ID from `settings.yaml`
- This is used in W5 when the two-step linked-grove create needs to find the co-located broker

---

## Phase 1 — Cosmetic Identity (W2) + Developer Token Relabel (W3)

### 1.1 — Identity fields on DevAuthConfig / V1AuthConfig (W2)

Files:
- `pkg/config/hub_config.go` — `DevAuthConfig` struct (around line 142-158): add `Username`, `DisplayName`, `Email string` fields
- `pkg/config/settings_v1.go` — `V1AuthConfig` (around line 383-388): same additions
- `pkg/hub/devauth.go` — `DevUser` construction (around line 26-49):
- **Keep the stable UUID** (`be67fbc9-...`) unchanged
- Read `Username`/`DisplayName`/`Email` from config
- If unset, default to OS user via `os/user` (`user.Current()` → `u.Username`, `u.Name`)
- Email default: `<osusername>@localhost`

Add a small `PUT /api/v1/system/identity` endpoint:
- Body: `{ "displayName": "...", "email": "..." }`
- Writes to `DevAuthConfig` in `settings.yaml` via the config save path
- Returns the updated identity
- Protected by `requireWorkstation`

### 1.2 — "Developer token" relabel (W3)

This is a text/copy pass only — **no code logic changes**:
- `cmd/server_daemon.go` `printWorkstationQuickstart()` (line 361-384): change "dev token" → "developer token" in the printed output
- CLI help strings referencing "dev token" in `cmd/` — update to "developer token"
- Web copy in `web/src/` — grep for "dev token" / "dev-token" in UI strings and update display text (not variable names, not `scion_dev_` format, not env var names — those stay)
- Any user-facing docs in `docs-site/` or `docs-repo/`

---

## Commit Instructions

- Commit Phase 0 work as one commit: `feat: add Workstation flag and workstation-only middleware to ServerConfig`
- Commit Phase 1 work as two commits:
- `feat: make dev identity configurable, default to OS user (W2)`
- `chore: relabel dev token as "developer token" in UI and docs (W3)`
- Run `go build ./...` and `go vet ./...` before each commit to verify no compile errors
- Do not open PRs — commit directly to the `workstation-improvements` branch
89 changes: 89 additions & 0 deletions .tasks/phase-2-system-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Phase 2: System API Endpoints

**Branch:** workstation-improvements
**Design docs:** `.design/workstation-onboarding-wizard.md` §6, `.design/workstation-onboarding.md` §7
**Prereq:** Phase 0+1 complete (Workstation flag and requireWorkstation middleware exist)
**Commit all changes to the current branch as you go.**

---

## Overview

Add thin `GET/PUT /api/v1/system/*` API endpoints that wrap existing Go logic. All are gated by `requireWorkstation` (404 in production). All require normal auth.

---

## 2.1 — Refactor doctor into a returnable function + `GET /system/check`

- In `cmd/doctor.go` (line 30-261) or `pkg/runtime/doctor.go` (line 15-41), extract the core check logic into a function `GatherDiagnostics(ctx context.Context, cfg *config.Config) ([]DiagnosticResult, error)` that returns structured results instead of printing them.
- `DiagnosticResult` struct: `{ Name, Status ("pass"|"warn"|"fail"), Message string }`
- Add `GET /api/v1/system/check` handler:
- Calls `GatherDiagnostics`
- Returns JSON: `{ "results": [...], "ready": bool }` where `ready` = no "fail" results

## 2.2 — `GET` and `PUT /system/runtime`

- `GET /api/v1/system/runtime`:
- Calls `config.DetectLocalRuntime()` (`pkg/config/runtime_detect.go:52-75`)
- Returns: `{ "detected": "docker"|"podman"|"container", "configured": "...", "available": bool }`
- `PUT /api/v1/system/runtime`:
- Body: `{ "runtime": "docker"|"podman"|"container" }`
- Validates the choice, writes `active_profile` (or runtime setting) to `settings.yaml`
- Returns the updated runtime config

## 2.3 — `ComputeOnboardingStatus` + `GET /system/status`

Compute a struct describing the onboarding state of the machine:

```go
type OnboardingStatus struct {
Initialized bool // ~/.scion/settings.yaml exists
IdentitySet bool // DevAuthConfig has username set (non-default)
RuntimeOK bool // a runtime is detected and reachable
HarnessesSeeded bool // at least one harness-config exists
ImagesPresent bool // at least one harness image is present (optional check)
HasWorkspace bool // at least one project exists
Complete bool // all required steps done
}
```

- `GET /api/v1/system/status` returns this struct as JSON
- Used by the frontend to detect first-run and resume the wizard

Also wire up `PUT /api/v1/system/identity` here if not done in Phase 1:
- Body: `{ "displayName": "...", "email": "..." }`
- Writes to `DevAuthConfig`, returns updated identity

## 2.4 — `POST /system/init`

- Body: `{ "harnesses": ["claude", "gemini", "codex", "opencode"] }` (subset allowed)
- Calls `config.InitMachine()` (`pkg/config/init.go:548-620`) if not already initialized
- Seeds only the selected harness-configs (filter the full seed set)
- Returns `{ "ok": true, "initialized": true }`
- Idempotent: safe to call on an already-initialized machine (no-op or partial re-seed)

---

## Route Registration

Register all endpoints in the existing `MountHubAPI` function (`pkg/hub/web.go:518-527`), wrapped with `requireWorkstation`:

```go
// System / onboarding endpoints (workstation only)
r.With(s.requireWorkstation).Get("/system/status", s.handleSystemStatus)
r.With(s.requireWorkstation).Get("/system/check", s.handleSystemCheck)
r.With(s.requireWorkstation).Get("/system/runtime", s.handleGetRuntime)
r.With(s.requireWorkstation).Put("/system/runtime", s.handlePutRuntime)
r.With(s.requireWorkstation).Post("/system/init", s.handleSystemInit)
r.With(s.requireWorkstation).Put("/system/identity", s.handlePutIdentity)
```

Put handler implementations in a new file: `pkg/hub/system_handlers.go`

---

## Commit Instructions

- One commit per logical unit is fine, or bundle as: `feat: add /system/* API endpoints for workstation onboarding (W1/W2)`
- Run `go build ./...` and `go vet ./...` before committing
- Do not open PRs — commit directly to `workstation-improvements`
Loading
Loading