integration

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package integration defines the contract between openPE's local HTTP server and third-party IDE patch installers (Windsurf, Cursor, VS Code Composer, ...).

The package intentionally does not import any IDE-specific code. Each IDE installer lives in its own sibling subproject under extensions/openpe-*-patch/ and implements the contracts defined here.

Concepts:

  • LocalServerDescriptor — handshake metadata exchanged via openPE's local server. Each installer reads this to learn the server's base URL and bearer token.
  • Token primitives — GenerateToken / TokensEqual / ValidateTokenShape provide a small constant-time-safe surface for the auth middleware and installers to share.
  • InjectorContract — uniform install/uninstall/status surface every IDE installer satisfies. The Go interface is used by shared tooling, tests, and future Go-native installers; Python installers honour the same conceptual contract documented here.
  • BundlePatcher — generic Electron bundle marker + backup logic. Idempotent marker placement, atomic file writes, and SHA-256 checksums.

Stability: this package is internal/ but its design is the canonical reference for any future IDE patch installer subproject. Backwards incompatible changes must be coordinated with every existing installer.

Disclaimer: IDE bundle patching is an experimental, opt-in capability that modifies third-party software and may violate the host IDE's EULA. Installers built on this package MUST require an explicit user disclaimer acceptance before performing any mutation.

Index

Constants

View Source
const TokenByteLength = 32

TokenByteLength is the entropy size used for bearer tokens. 32 bytes (256 bits) is well beyond the 128-bit floor recommended by OWASP for session identifiers.

View Source
const TokenStringLength = TokenByteLength * 2

TokenStringLength is the length of the hex-encoded token string.

Variables

View Source
var ErrDisclaimerRequired = disclaimerError{}

ErrDisclaimerRequired is returned by Injector.Install when the caller has not set InstallOptions.DisclaimerAccepted to true. Front-ends should display the experimental + EULA risk text and require an explicit user confirmation before retrying with DisclaimerAccepted = true.

Functions

func DefaultDescriptorPath

func DefaultDescriptorPath() (string, error)

DefaultDescriptorPath returns the canonical descriptor file location. Resolution order:

  1. OPENPE_SERVER_DESCRIPTOR_FILE — explicit override.
  2. XDG_CONFIG_HOME/openpe/server.json — XDG-aware desktops.
  3. <user home>/.config/openpe/server.json — fallback.

func GenerateToken

func GenerateToken() (string, error)

GenerateToken returns a fresh hex-encoded bearer token sourced from crypto/rand. The returned string is TokenStringLength characters long and contains only lower-case hex digits.

func RemoveDescriptor

func RemoveDescriptor(path string) error

RemoveDescriptor deletes the descriptor file if it exists. A missing file is not treated as an error.

func RemoveDescriptorIfOwned

func RemoveDescriptorIfOwned(path string, pid int, token string) error

RemoveDescriptorIfOwned deletes the descriptor only when it still belongs to this instance (same PID and token). A second openpe-server that failed to bind must not tear down the running instance's descriptor on its way out: the file it would delete is the ONLY discovery channel IDE installers have. A missing file is fine; a foreign or unreadable file is left in place — leaving a stale file behind is recoverable, deleting a live sibling's descriptor is not.

The read-verify-remove sequence runs under the descriptor's cross-process lock, shared with WriteDescriptor: without it a sibling could republish between this function's read and its remove, and the wrong descriptor would be deleted despite the ownership check.

func TokensEqual

func TokensEqual(a, b string) bool

TokensEqual compares two bearer tokens in constant time, returning true only when both are non-empty, the same length, and identical. Empty tokens never match anything (defence against accidental zero-value comparisons).

func ValidateTokenShape

func ValidateTokenShape(token string) error

ValidateTokenShape ensures a token string looks like one produced by GenerateToken. Used to reject obviously malformed values before any timing-sensitive comparison.

func WriteDescriptor

func WriteDescriptor(path string, d LocalServerDescriptor) error

WriteDescriptor atomically persists d to path with mode 0600. Parent directories are created with mode 0700. The write uses a temp file + rename so concurrent readers always see either the previous or the new payload, and holds the descriptor's cross-process lock so a sibling's ownership-checked cleanup can never interleave with this publish (the read-check-remove TOCTOU: A verifies ownership, B replaces the file, A removes B's descriptor).

Types

type BundlePatcher

type BundlePatcher interface {
	// HasMarker reports whether the bundle currently contains both
	// marker delimiters.
	HasMarker(bundlePath string, marker Marker) (bool, error)
	// Inject writes payload into the bundle, replacing any existing marker
	// region in place. Atomic on success.
	Inject(bundlePath, payload string, marker Marker) error
	// Restore copies backupPath onto bundlePath atomically.
	Restore(bundlePath, backupPath string) error
	// Backup copies bundlePath into backupDir, returning the absolute path
	// of the new backup file. Existing backups are kept (timestamp suffix).
	Backup(bundlePath, backupDir string) (backupFile string, err error)
	// Checksum returns the lower-case hex SHA-256 of path.
	Checksum(path string) (string, error)
}

BundlePatcher is the generic surface for read / backup / inject / restore on an Electron bundle. All IDE installers share this implementation; only path resolution differs per IDE.

type FilePatcher

type FilePatcher struct{}

FilePatcher is the default file-system based BundlePatcher implementation. It performs all mutations atomically via temp file + rename and refuses to inject empty payloads or malformed markers.

func NewFilePatcher

func NewFilePatcher() FilePatcher

NewFilePatcher returns the default FilePatcher.

func (FilePatcher) Backup

func (FilePatcher) Backup(bundlePath, backupDir string) (string, error)

Backup copies bundlePath into backupDir with a timestamp suffix and returns the absolute path of the new file. The backup is written with mode 0600 to discourage tampering.

func (FilePatcher) Checksum

func (FilePatcher) Checksum(path string) (string, error)

Checksum returns the lower-case hex SHA-256 of path.

func (FilePatcher) HasMarker

func (FilePatcher) HasMarker(bundlePath string, marker Marker) (bool, error)

HasMarker reports whether bundlePath contains exactly one well-formed marker region. The semantics mirror the Python installer's bundle.has_marker byte for byte: zero pairs is false, exactly one ordered pair is true, and duplicated or out-of-order delimiters are an ERROR — the two implementations claim to be mirrors, and the old Contains-only check answered true for bundles the Python side rejects as malformed.

func (FilePatcher) Inject

func (FilePatcher) Inject(bundlePath, payload string, marker Marker) error

Inject writes payload into bundlePath wrapped by marker. When an existing marker region is found, its body is replaced in place; otherwise the new block is appended. The write is atomic via temp file + rename. A payload that itself contains the marker delimiters is rejected: injecting it would produce duplicated markers that every later HasMarker/inject pass refuses to touch.

func (FilePatcher) Restore

func (FilePatcher) Restore(bundlePath, backupPath string) error

Restore copies backupPath onto bundlePath atomically.

type IDEPaths

type IDEPaths struct {
	// AppRoot is the IDE installation root, e.g. /Applications/Windsurf.app
	// on macOS or C:\Users\<u>\AppData\Local\Programs\Windsurf on Windows.
	AppRoot string
	// BundleFile is the absolute path to the Electron bundle that receives
	// the injection (typically workbench.desktop.main.js).
	BundleFile string
	// ProductFile is the absolute path to product.json, used to bypass the
	// Electron resource checksum guard.
	ProductFile string
	// BackupDir is the installer-local directory where the original bundle
	// is preserved before injection. Restoring from this directory must
	// recover the IDE to its pre-injection state byte-for-byte.
	BackupDir string
}

IDEPaths describes where a particular IDE keeps its Electron bundle and supporting files on the current machine. PathResolver implementations in each installer subproject return one of these.

type InjectStatus

type InjectStatus struct {
	// Injected is true when the live bundle currently contains the openPE
	// injection markers.
	Injected bool
	// InjectVersion is the version string recorded inside the marker meta,
	// empty when not injected.
	InjectVersion string
	// BackupExists is true when a usable backup file is present in the
	// installer-local backup directory.
	BackupExists bool
	// IDEVersion is the version reported by the IDE's product.json, or
	// empty when unavailable.
	IDEVersion string
	// LiveChecksum is the SHA-256 of the current on-disk bundle.
	LiveChecksum string
	// BackupChecksum is the SHA-256 of the most recent backup, or empty
	// when no backup exists.
	BackupChecksum string
}

InjectStatus reports the current state of an IDE injection. Returned by Injector.Status without performing any mutation.

type Injector

type Injector interface {
	// Name returns the lower-case IDE identifier, e.g. "windsurf", "cursor".
	Name() string
	// ResolvePaths discovers the IDE's install paths on the current machine.
	// Honours opts.AppDirOverride when non-empty.
	ResolvePaths(ctx context.Context, opts InstallOptions) (*IDEPaths, error)
	// Status reports the current injection state without mutating anything.
	Status(ctx context.Context, opts InstallOptions) (*InjectStatus, error)
	// Install performs the injection. Must refuse unless
	// opts.DisclaimerAccepted is true.
	Install(ctx context.Context, opts InstallOptions) error
	// Uninstall restores the IDE to its pre-injection state from the backup.
	Uninstall(ctx context.Context, opts InstallOptions) error
}

Injector is the uniform surface every IDE patch installer satisfies. Each subproject (extensions/openpe-*-patch/) typically wraps a native Python installer; this Go interface is used by shared tooling, Go-side tests, and any future Go-native installers.

Implementations must:

  • Be safe to call concurrently from at most one goroutine (no internal locking required; callers serialise install/uninstall).
  • Treat Install/Uninstall as idempotent: re-running Install when already injected must not nest markers; Uninstall when nothing is injected must be a no-op success.
  • Refuse Install when InstallOptions.DisclaimerAccepted is false.

type InstallOptions

type InstallOptions struct {
	// AppDirOverride lets the user point the installer at a non-default IDE
	// install location (e.g. portable installs or sandboxed test fixtures).
	AppDirOverride string
	// DryRun reports the actions that would be taken without touching disk.
	DryRun bool
	// DisclaimerAccepted MUST be set to true by the CLI front-end after the
	// user has explicitly acknowledged the experimental + EULA risk. Installers
	// must refuse to mutate state when this is false.
	DisclaimerAccepted bool
}

InstallOptions controls how an Injector performs its work. DisclaimerAccepted MUST be true for any mutating call to succeed.

type LocalServerDescriptor

type LocalServerDescriptor struct {
	// BaseURL is the loopback HTTP endpoint, e.g. http://127.0.0.1:18980.
	BaseURL string `json:"base_url"`
	// Token is the bearer token clients must send as Authorization header.
	Token string `json:"token"`
	// PID is the openpe-server process identifier; installers can check
	// liveness with os.FindProcess + signal 0.
	PID int `json:"pid"`
	// StartedAt is the server start time in RFC3339 format.
	StartedAt string `json:"started_at"`
	// Version is the openpe-server build version string, empty when unset.
	Version string `json:"version,omitempty"`
}

LocalServerDescriptor is the handshake payload openPE's local HTTP server exposes to IDE installers running on the same host.

The descriptor is normally persisted to ~/.config/openpe/server.json with file mode 0600. IDE installers read this file (or call GET /v1/info while authenticated) to learn the base URL and bearer token.

func NewLocalServerDescriptor

func NewLocalServerDescriptor(baseURL, token string, pid int, version string) LocalServerDescriptor

NewLocalServerDescriptor constructs a descriptor with the supplied fields and a StartedAt timestamp set to time.Now().UTC().

func ReadDescriptor

func ReadDescriptor(path string) (LocalServerDescriptor, error)

ReadDescriptor loads a descriptor previously written by WriteDescriptor. It refuses to read files whose mode is broader than 0600 because they may have leaked the bearer token to other local users.

func (LocalServerDescriptor) Validate

func (d LocalServerDescriptor) Validate() error

Validate ensures all required fields are present and well-formed.

type Marker

type Marker struct {
	Begin string
	End   string
}

Marker delimits an injection inside a host bundle. Markers are designed to be:

  • Easy for humans to spot inside a multi-MB minified JS file.
  • Strict enough that two unrelated tools do not accidentally cross- recognise each other's markers.
  • Idempotent: a second Inject MUST detect an existing marker and replace its body in place rather than nesting markers.

func DefaultMarker

func DefaultMarker() Marker

DefaultMarker returns the canonical openPE injection markers used by all IDE installers.

func (Marker) Validate

func (m Marker) Validate() error

Validate ensures both delimiters are non-empty and unequal.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL