# Architecture ## Process model ```text +---------------- Electron renderer ----------------+ | Desktop UI | | No Node.js, filesystem, process or token access | +-------------------------+---------------------------+ | frozen preload API | +-------------------------v---------------------------+ | Electron main process | | | | IPC validation + trusted sender checks | | ConfigStore -------- schema 8 + protected Gitea/SSH secrets | | GitService ----------- Git through execFile args | | GiteaService --------- repositories + Actions API | | RepositoryService ---- discovery + aggregation | | RepositoryMonitor ---- local state awareness | | PreflightService ----- system/deployment readiness | | DeploymentService ---- dispatch/poll/verify | | DiagnosticsService --- JSONL/redaction/support ZIP | +----------------+--------------------+---------------+ | | local Git Gitea API | | working trees repositories/actions | trusted runner | fixed allowlisted entry point | app + independent status JSON ``` ## Trust boundaries ### Renderer The renderer is untrusted input. It can request only methods exposed by `preload.cjs`. Node integration is disabled, context isolation and sandboxing are enabled, and IPC requests are accepted only from the packaged file origin. Renderer crashes and unhandled rejections are reported through a sanitized one-way diagnostic method. ### Main process Paths, URLs, file selections, branch names, commit messages, diagnostic export modes and deployment requests are checked here. The renderer cannot supply a server command. UI eligibility is advisory; main-process services re-read the working tree and remote ancestry immediately before privileged actions. ### Gitea Gitea supplies repository metadata and the Actions control plane. ForgeFlow handles run-list response variants, retries servers that reject optional query filters and can fall back to the older tasks listing. Requests log only method, API path, status and duration—not authorization headers or request bodies. ### Runner and server The runner consumes only committed trusted workflows. The root-owned server entry point reads an exact repository/environment target from a root-owned, non-writable data file. It validates the full SHA again and uses a per-environment lock. The runner receives no free-form command from ForgeFlow. ## Local state `forgeflow-config.json` lives below Electron's platform-specific user-data path and is written atomically. Schema version 8 contains: - Gitea connection metadata and an OS-encrypted token blob where available; - workspace roots and explicit repository mappings; - favorites and application preferences; - diagnostic retention and level preferences; - multiple deployment profiles and last server state; - up to 250 operation records. Renderer-visible public state never contains the plaintext or encrypted token. Structured diagnostic JSONL files live in a separate `diagnostics` directory. They have independent rotation and retention and never block normal app use when logging itself fails. ## Repository aggregation 1. Fetch accessible Gitea repositories. 2. Scan bounded workspace roots for Git working trees. 3. Normalize HTTPS and SCP-style SSH remotes. 4. Match local `origin` identity to `owner/repository`. 5. Apply explicit mappings where present. 6. Read Git state with bounded concurrency. 7. Attach favorites, deployment profiles and last server state. 8. Derive attention and ready-to-deploy status. 9. Feed linked paths to the repository monitor. ## Repository clone lifecycle The first configured project root is the default. The renderer submits only the current Gitea repository identity and either `default` or `custom` location mode. The main process resolves the repository again, selects a configured root or a native-dialog result, calculates the repository-named child path and asks GitService to inspect it. ```text repository identity | current Gitea metadata | project root + safe repository folder name | missing / empty / matching checkout / conflict | clone or reuse -> save mapping -> refresh -> monitor ``` A matching existing checkout is reused. Different repositories and arbitrary non-empty folders are rejected. ## Git execution ForgeFlow invokes the installed Git executable through `execFile`; it never builds shell command strings. File arguments must remain repository-relative and cannot contain traversal segments. Core status command: ```bash git status --porcelain=v2 --branch -z --untracked-files=all ``` Synchronization is intentionally limited to: ```bash git pull --ff-only ``` Branch switching requires a clean working tree. Stash supports untracked files. Deployment and rollback verify the full SHA against `origin/` with `merge-base --is-ancestor`. ## Preflight model ### System preflight Checks Git, author identity, app storage, diagnostic storage, OS credential protection, configured workspace roots and—when credentials are present—Gitea connectivity and repository visibility. ### Deployment preflight Checks repository link, Git working tree, allowed branch, clean state, upstream, ahead/behind state, exact remote SHA, local and remote workflow presence, Actions API access, server status endpoint and healthcheck. Only failed required checks block readiness. The deployment backend repeats safety-critical Git/SHA validation after the user continues. ## Repository monitor The current monitor periodically fingerprints Git state. It establishes a baseline, reports later changes and pauses during mutating operations to avoid intermediate noise. It is dependency-free rather than a native filesystem watcher. ## Deployment lifecycle ```text requested -> queued -> running -> health/version verification -> terminal ``` A deployment operation stores the exact SHA, fixed profile, environment, workflow and a UUID request ID. Polling then: 1. finds the matching Actions run by SHA, branch, workflow and dispatch time; 2. normalizes run status; 3. retrieves jobs and locally redacted runner output; 4. maps jobs to ForgeFlow stages; 5. reads the independent status endpoint and healthcheck after runner success; 6. verifies live SHA equality; 7. stores success, rolled-back, failed or cancelled. The request ID is sent to the workflow and server status document, making one operation correlatable without using a credential as an identifier. ## Diagnostics pipeline ```text event -> recursive sanitization -> ordered JSONL write | support export requested | fresh state + preflight + redacted logs | strict/standard privacy transformation | fail-closed local secret safety audit | ZIP + SHA-256 result ``` Raw runner output is deliberately omitted from exported support bundles. ## Status endpoint The recommended endpoint is a static JSON file served independently from the application. It reports live, previous and requested SHAs, request ID, health and last exit code. See `STATUS_ENDPOINT.md`. ## v0.4 services - `UpdateService` reports `package.json` at an exact Gitea branch SHA, refuses unsigned source replacement and applies only publisher-signed packaged updates. - `SshService` provides pinned-host SSH execution with encrypted password or private-key passphrase storage. - `UnraidDeploymentService` inspects existing application folders and performs exact-SHA Git and Docker Compose deployments without deleting untracked runtime data.