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
24 changes: 24 additions & 0 deletions cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Handlers struct {
RunReviewCleanup cli.ActionFunc
RunAttestationTrailer cli.ActionFunc
RunSetup cli.ActionFunc
RunOnboard cli.ActionFunc
RunUI cli.ActionFunc
RunUsageInspect cli.ActionFunc
RunInternalClaudePreToolUse cli.ActionFunc
Expand Down Expand Up @@ -321,6 +322,29 @@ func BuildApp(version, buildTime, gitCommit, reviewMode string, baseFlags, debug
},
Action: h.RunSetup,
},
{
Name: "onboard",
Usage: "Perform non-interactive onboarding using an onboarding API key",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "api-url",
Aliases: []string{"base-url"},
Usage: "override LiveReview API base URL for onboarding",
},
&cli.StringFlag{
Name: "onboarding-key",
Aliases: []string{"key"},
Usage: "onboarding API key to use for authentication",
Required: true,
EnvVars: []string{"LRC_API_KEY"},
},
&cli.BoolFlag{
Name: "yes",
Usage: "run non-interactively; replaces config file if already exists without confirmation",
},
},
Action: h.RunOnboard,
},
{
Name: "ui",
Usage: "Open local web UI to manage your git-lrc",
Expand Down
110 changes: 110 additions & 0 deletions internal/appui/onboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package appui

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"strings"
"time"

"github.com/HexmosTech/git-lrc/network"
setuptpl "github.com/HexmosTech/git-lrc/setup"
"github.com/urfave/cli/v2"
)

// RunOnboard handles the "lrc onboard" command.
func RunOnboard(c *cli.Context) error {
// 1. Resolve API URL
apiURL := strings.TrimSpace(c.String("api-url"))
if apiURL == "" {
apiURL = os.Getenv("LRC_API_URL")
}
if apiURL == "" {
apiURL = setuptpl.CloudAPIURL
}
apiURL = strings.TrimRight(apiURL, "/")

// 2. Resolve Onboarding API Key
onboardingKey := strings.TrimSpace(c.String("onboarding-key"))
if onboardingKey == "" {
return errors.New("onboarding API key cannot be empty; pass via --onboarding-key or LRC_API_KEY env var")
}

// 3. Check existing config details
details, err := setuptpl.ReadExistingConfigDetails()
if err != nil {
return fmt.Errorf("failed to read existing config details: %w", err)
}

// If config exists and we are interactive / no "--yes" flag, ask the user
if details.Exists && !c.Bool("yes") && isInteractiveSetupStdin() {
replace, err := promptSetupYesNo(" Existing config file detected. Replace it?", false)
if err != nil {
return err
}
if !replace {
fmt.Println(" Onboarding cancelled. Existing configuration preserved.")
return nil
}
}

fmt.Printf(" Onboarding to LiveReview server at %s...\n", apiURL)

// 4. Hit the onboard endpoint
client := network.NewClient(30 * time.Second)
resp, err := network.SetupOnboard(client, apiURL, onboardingKey)
if err != nil {
return fmt.Errorf("failed to contact LiveReview API: %w", err)
}
if resp.StatusCode != http.StatusOK {
var errorResp struct {
Error string `json:"error"`
}
if err := json.Unmarshal(resp.Body, &errorResp); err == nil && errorResp.Error != "" {
return fmt.Errorf("onboarding failed (status %d): %s", resp.StatusCode, errorResp.Error)
}
return fmt.Errorf("onboarding failed (status %d)", resp.StatusCode)
}

// 5. Parse response
var onboardResp struct {
APIKey string `json:"api_key"`
OrgID json.Number `json:"org_id"`
OrgName string `json:"org_name"`
JWT string `json:"jwt"`
RefreshToken string `json:"refresh_token"`
}
if err := json.Unmarshal(resp.Body, &onboardResp); err != nil {
return fmt.Errorf("failed to parse onboarding response: %w", err)
}

// 6. Backup config if it exists
if details.Exists {
backupPath, err := setuptpl.BackupExistingConfig(nil)
if err != nil {
return fmt.Errorf("failed to backup existing config: %w", err)
}
if backupPath != "" {
fmt.Printf(" 📦 Backed up existing config to %s\n", backupPath)
}
}

// 7. Write new configuration
result := &setuptpl.SetupResult{
PlainAPIKey: onboardResp.APIKey,
OrgID: onboardResp.OrgID.String(),
OrgName: onboardResp.OrgName,
AccessToken: onboardResp.JWT,
RefreshToken: onboardResp.RefreshToken,
}

if err := setuptpl.WriteConfigWithOptions(result, setuptpl.WriteConfigOptions{APIURL: apiURL}); err != nil {
return fmt.Errorf("failed to write configuration: %w", err)
}

fmt.Println(" ✅ Onboarding completed successfully!")
fmt.Printf(" Configuration saved to %s\n", details.Path)
return nil
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func main() {
RunReviewCleanup: func(c *cli.Context) error { return reviewdb.RunReviewDBCleanup(c.Bool("verbose")) },
RunAttestationTrailer: appcore.RunAttestationTrailer,
RunSetup: appui.RunSetup,
RunOnboard: appui.RunOnboard,
RunUI: appui.RunUI,
RunUsageInspect: appcore.RunUsageInspect,
RunInternalClaudePreToolUse: appcore.RunInternalClaudePreToolUse,
Expand Down
4 changes: 4 additions & 0 deletions network/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ func SetupEnsureCloudUserURL(baseURL string) string {
return strings.TrimSuffix(baseURL, "/") + "/api/v1/auth/ensure-cloud-user"
}

func SetupOnboardURL(baseURL string) string {
return strings.TrimSuffix(baseURL, "/") + "/api/v1/auth/onboard"
}

func SetupAuthLoginURL(baseURL string) string {
return strings.TrimSuffix(baseURL, "/") + "/api/v1/auth/login"
}
Expand Down
12 changes: 7 additions & 5 deletions network/network_status.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This document tracks network-side operations in git-lrc as an auditable inventor

- Network boundary: outbound HTTP API operations and response handling in network package.
- Modes represented: api.
- Operation count tracked: 22 operations.
- Operation count tracked: 24 operations.
- Severity distribution: High 10, Medium 7, Low 2.
- Current diff note: self-hosted setup now uses LiveReview email/password auth endpoints (`/api/v1/auth/login`, `/api/v1/auth/setup-status`, `/api/v1/auth/setup`) in addition to existing cloud ensure-cloud-user setup path.
- Current diff note: internal reviewapi helper evidence links were revalidated after git path helper additions; network inventory scope is unchanged.
Expand Down Expand Up @@ -53,6 +53,7 @@ This document tracks network-side operations in git-lrc as an auditable inventor
| SetupValidateConnectorKey | api | Provider key and validation request body | Validate AI connector key before persistence | High | Third-party key exposure/handling risk | Compensated by authenticated request path plus connector key redaction in setup error surfaces; residual risk acceptable | [network/setup_operations.go](setup_operations.go#L41) |
| SetupCreateConnector | api | Connector configuration payload | Persist connector configuration via LiveReview API | High | Misconfiguration and sensitive metadata transmission risk | Compensated by bearer auth plus org context boundary; residual risk acceptable | [network/setup_operations.go](setup_operations.go#L46) |
| SetupListConnectors | api | Existing connector inventory for current org | Inspect minimum AI readiness during setup and re-auth preflight | High | Sensitive connector metadata and auth-context exposure risk | Compensated by bearer auth plus org context boundary; residual risk acceptable | [network/setup_operations.go](setup_operations.go#L51) |
| SetupOnboard | api | Onboarding API key (X-API-Key header) and returned token set | Non-interactively onboard a new machine using an onboarding API key | High | Exposure of onboarding key and returned credentials | Compensated by direct, secure binary execution, atomic config writes (0600), and short-lived setup context; residual risk acceptable | [network/setup_operations.go](setup_operations.go#L56) |

## Inventory: Proxy And Forwarding APIs

Expand All @@ -76,10 +77,11 @@ This document tracks network-side operations in git-lrc as an auditable inventor
| Client.DoJSON | api | Request/response JSON payload bytes | Standard JSON HTTP call wrapper | Medium | Medium risk from broad transport usage and status-handling variance | Compensated by centralized transport wrapper with timeout controls; acceptable risk | [network/http_client.go](http_client.go#L43) |
| Client.Do | api | Raw HTTP request/response bytes | Generic HTTP call wrapper for non-JSON/raw workflows | Medium | Medium risk from raw payload handling flexibility | Partially compensated by shared client boundary; Suggestion: document callsite expectations for raw bodies | [network/http_client.go](http_client.go#L89) |
| SetupEnsureCloudUserURL | api | Base URL plus endpoint normalization inputs | Normalize endpoint composition and reduce path ambiguity | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L13) |
| SetupAuthLoginURL | api | Base URL plus endpoint normalization inputs | Build self-hosted login endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L17) |
| SetupUIConfigURL | api | Base URL | Build UI config URL | Low | Low risk | Compensated by public endpoint utility; acceptable risk | [network/endpoints.go](endpoints.go#L21) |
| SetupAuthSetupStatusURL | api | Base URL plus endpoint normalization inputs | Build self-hosted setup-status endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L25) |
| SetupAuthSetupURL | api | Base URL plus endpoint normalization inputs | Build self-hosted initial-admin setup endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L29) |
| SetupOnboardURL | api | Base URL plus endpoint normalization inputs | Build onboarding endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L17) |
| SetupAuthLoginURL | api | Base URL plus endpoint normalization inputs | Build self-hosted login endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L21) |
| SetupUIConfigURL | api | Base URL | Build UI config URL | Low | Low risk | Compensated by public endpoint utility; acceptable risk | [network/endpoints.go](endpoints.go#L25) |
| SetupAuthSetupStatusURL | api | Base URL plus endpoint normalization inputs | Build self-hosted setup-status endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L29) |
| SetupAuthSetupURL | api | Base URL plus endpoint normalization inputs | Build self-hosted initial-admin setup endpoint URL | Medium | Medium risk if normalization logic diverges from endpoint assumptions | Compensated by centralized URL builder utility; acceptable risk | [network/endpoints.go](endpoints.go#L33) |
| PollReview | api | Review IDs, status payloads, timeout state | Timeout-bounded polling orchestration in review runtime | High | High availability/latency risk if review service is degraded | Compensated by bounded timeout and interval controls; residual risk acceptable | [internal/reviewapi/helpers.go](../internal/reviewapi/helpers.go#L201) |
| formatJSONParseError | api | Response body text for parse diagnostics | Improve operator diagnostics when endpoint/port mismatches occur | Low | Low risk diagnostic utility behavior | Compensated by safer error interpretation path; acceptable risk | [internal/reviewapi/helpers.go](../internal/reviewapi/helpers.go#L129) |

Expand Down
7 changes: 7 additions & 0 deletions network/setup_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,10 @@ func SetupCreateConnector(client *Client, cloudBase, orgID string, payload any,
func SetupListConnectors(client *Client, cloudBase, orgID, accessToken string) (*Response, error) {
return client.DoJSON(http.MethodGet, SetupCreateConnectorURL(cloudBase), nil, accessToken, orgID, nil)
}

// SetupOnboard submits the onboarding request to LiveReview.
func SetupOnboard(client *Client, cloudBase, onboardingKey string) (*Response, error) {
return client.DoJSON(http.MethodPost, SetupOnboardURL(cloudBase), nil, "", "", map[string]string{
"X-API-Key": onboardingKey,
})
}
Loading
Loading