Skip to content

Operations Guide

IMPORTANT

This page has two halves, and most people only need the first.Part 1 is operating an installed LoopTroop: the service, its state, backups, worktree cleanup. Part 2 is the development stack — running from a checkout with npm run dev to work on LoopTroop itself. The preflight, maintenance, dependency and script material in Part 2 does not apply to an installed copy, and several of its commands are not even present in one.


Part 1: Operating An Installed LoopTroop

An installed LoopTroop runs as a background service:

bash
looptroop open       # starts it if it is not running, then opens a browser
looptroop status     # --json for a script
looptroop logs -f
looptroop restart
looptroop stop

Every command and option is in the CLI Reference; installing, upgrading and uninstalling are in Installation.

Where an installed LoopTroop keeps its state

Everything lives in one configuration directory, outside the installation — so upgrading, or switching channels entirely, never loses it. The directory is 0700 and the files in it 0600.

FileWhat it is
config.jsonSettings you have changed from the defaults
app.sqliteApp settings, profiles, and the attached-project registry
daemon.jsonThe running daemon's record: pid, port, instance id, and the API token it minted at startup. Also records why the last start was refused
daemon.lockHeld by the running daemon, so a second one cannot start against the same directory
logs/daemon.logWhat looptroop logs reads. Rotated at each start

Backing up is copying that directory with the daemon stopped. Your projects are not in it: LoopTroop works in git worktrees under <project>/.looptroop/, described in Part 2's storage table, which applies to both stacks.

Routine tasks

TaskCommand
Check the machine can run itlooptroop doctor — see Runtime Diagnostics
Find out whether an update exists and how to apply itlooptroop doctor shows current/latest versions, names the channel, and prints its ordered upgrade and restart steps
Remove worktrees left by cancelled ticketslooptroop clean, then looptroop clean --apply

looptroop clean is worktree housekeeping: it removes git worktrees left behind by cancelled or interrupted tickets, inside your project, and never touches configuration, tickets or the database. It refuses to run while the daemon is up, because those worktrees may be in use.

One daemon runs per configuration directory, held by a lock that records which process took it rather than only when it last checked in. To run two, give each its own LOOPTROOP_CONFIG_DIR and port.

OpenCode is managed for you

An installed daemon does not need you to run opencode serve. At startup it either adopts an OpenCode already listening at the configured base URL, or starts and supervises one itself, restarting it if it crashes and stopping it when the daemon stops. With no OpenCode to reach and no CLI to launch, the daemon refuses to start rather than serving an interface that cannot run a single coding operation — LOOPTROOP_OPENCODE_MODE=mock looks around without one.

looptroop doctor reports which of those happened.


Part 2: The development stack

Everything from here on is about running LoopTroop from a checkout, to work on LoopTroop itself. See Working on LoopTroop itself to set it up.

1. Quick Reference

TaskStart here
Start the full local stacknpm run dev
Start once with dependency/audit maintenanceLOOPTROOP_DEV_MAINTENANCE=1 npm run dev
Skip only the local OpenCode CLI upgradeLOOPTROOP_DEV_SKIP_OPENCODE_UPGRADE=1 npm run dev
Inherit your external OpenCode permission modeLOOPTROOP_OPENCODE_PERMISSION_MODE=inherit npm run dev
Share the dashboard on a trusted local networknpm run dev --lan
Print full managed OpenCode DEBUG logs in the terminalnpm run dev --opencode-logs=all
Force all startup maintenance nowLOOPTROOP_DEV_FORCE_MAINTENANCE=1 npm run dev
Diagnose slow UI or ticket refresh stallsnpm run diagnose:stall
Clean tracked LoopTroop runtime paths from a projectgit rm --cached -r .looptroop inside the attached project

2. Runtime Storage

LoopTroop deliberately separates app-level state from project-level runtime state.

LocationContentsNotes
~/.config/looptroop/app.sqliteApp settings, profiles, and attached-project registryOverride with LOOPTROOP_CONFIG_DIR or LOOPTROOP_APP_DB_PATH
<project>/.looptroop/db.sqliteProject tickets, phase artifacts, attempts, sessions, status history, and error occurrencesProject-local operational database
<project>/.looptroop/worktrees/<ticket>/Ticket-owned Git worktree and .ticket/** runtime artifactsOne worktree per ticket
<ticket-worktree>/.ticket/runtime/Execution logs, stream state, locks, session records, temporary files, and state projectionPreserved or cleaned according to ticket outcome and cleanup choice
<repo>/tmp/dev-preflight-report.jsonLast npm run dev preflight result: dependency sync, audit remediation, OpenCode upgrade, and install checksRebuilt on successful dev preflight; safe to delete
<repo>/tmp/dev-maintenance-state.jsonDaily maintenance timestamps and invalidation bookkeeping for dependency sync, audit remediation, and OpenCode upgradeLets normal startup defer already-run daily maintenance until relevant inputs change
~/.local/share/opencode/log/Default local OpenCode log directoryUsed for managed OpenCode DEBUG logs and generic provider-error enrichment unless LOOPTROOP_OPENCODE_LOG_DIR points elsewhere

When a project is attached, LoopTroop applies its saved folder-ignore policy to /.looptroop/ and /.ticket/. This clone (local) is the default and appends the rules to the clone's Git exclude file, normally .git/info/exclude, without modifying tracked files. Repository (repo) appends them to the project's tracked .gitignore, while Nowhere (skip) deliberately writes neither destination and leaves a visible warning. Ticket initialization reapplies the saved project policy; for non-skip projects, it uses the shared Git exclude only when a new worktree does not yet see effective rules. Existing rules are never removed automatically.

The tmp/*.json maintenance files are repository-local helpers, not durable source-of-truth data. Removing them only causes LoopTroop to regenerate them on the next relevant run.

3. Startup Maintenance

npm run dev starts the frontend, backend, and OpenCode watcher stack. Documentation is hosted separately at https://www.looptroop.ovh/docs/ and is not served from the application checkout.

Preflight responsibilities

Before those services launch, LoopTroop runs a dev preflight that:

  • prints immediate progress for bootstrap checks, daily maintenance, stale-process cleanup, and port availability so startup does not appear stalled during slower checks
  • restores missing local tooling with npm ci when dependencies need to be restored, then verifies required local dev binaries
  • checks direct dependencies against npm publish metadata (only when maintenance is opted in)
  • previews stale direct dependencies with npm's normal peer resolver and updates only compatible stable releases that are newer than the current installed version and at least 7 days old
  • holds newer releases that are still inside that 7-day delay or conflict with the current peer dependency graph; automatic maintenance never retries with --force or --legacy-peer-deps
  • previews npm audit fix lockfile changes with the same peer resolver, recognizes npm's expected exit code when unresolved findings remain, and runs the fix only when the proposal is compatible and every proposed npm package version has passed the same 7-day delay
  • retries temporary npm audit transport or malformed-response failures once, then defers that audit without stamping it complete so an external registry outage cannot prevent the application or a boot-enabled service from starting
  • upgrades the local opencode CLI to the latest available version when the binary is installed (only when maintenance is opted in)
  • checks and reclaims only stale LoopTroop-owned processes on configured ports
  • refuses to kill unrelated port occupants and reports which process still owns the conflicting port
  • writes the last successful preflight snapshot to tmp/dev-preflight-report.json
  • prints one concise startup summary by default, including package gate notes, updated package names, previous and new versions, held package names, and next eligible times

OpenCode, auth, and service bootstrap

npm run dev also resolves the local OpenCode server endpoint before the dev services launch:

  • Reuse: if the configured address is already responding to authenticated requests, npm run dev reuses that running instance.
  • Explicit base URL guard rail: if an explicitly configured local LOOPTROOP_OPENCODE_BASE_URL is occupied by a non-OpenCode process, startup stops and asks you to choose another URL. Automatic port fallback only applies to the default local address.
  • Port fallback: if the default OpenCode port (4096) is occupied by a non-OpenCode process, npm run dev scans for the next free port and starts OpenCode there instead.
  • Permission mode: when npm run dev starts the managed OpenCode server, it sets OPENCODE_PERMISSION='"allow"' by default. LoopTroop also applies a complete ordered permission policy to every session before each prompt, explicitly allowing external_directory and doom_loop for trusted unattended work before applying any phase-specific restrictions. If OpenCode still emits an unexpected permission request, LoopTroop answers it automatically with always; a failed reply aborts the session immediately so normal retry or blocked-error handling can proceed instead of leaving the ticket idle. Set LOOPTROOP_OPENCODE_PERMISSION_MODE=inherit to leave any existing OpenCode permission environment untouched; session-level policies still apply.
  • LAN and trusted same-origin proxies: start with npm run dev --lan only when exposing the frontend directly on a trusted local network. The startup summary prints LAN URLs and a QR code for mobile testing, while backend API and OpenCode remain loopback-only behind the Vite dev proxy. A trusted same-origin proxy such as Tailscale Serve can instead front the ordinary loopback Vite server. For either route, before forwarding an API request to the loopback backend, Vite normalizes Origin only when the browser marks the request as same-origin and its Origin authority matches the incoming frontend Host. An unrelated site's Origin stays unchanged and the backend rejects it. Documentation links continue to use the hosted site. Under WSL, LoopTroop does not start a relay process; it prints a Windows Administrator PowerShell netsh interface portproxy + firewall one-liner, matching cleanup commands, and a Windows-side self-test instead. If the matching Windows network profile is Public, LoopTroop also prints the exact Set-NetConnectionProfile ... -NetworkCategory Private fix command. Router/AP client isolation still has to be checked manually if Windows-side self-tests pass but other devices cannot connect.
  • Verbose OpenCode logs: start with npm run dev --opencode-logs=all to print full managed OpenCode DEBUG logs in your terminal via --print-logs --log-level DEBUG. Managed logs are also written to the normal OpenCode log directory. This only affects servers started by the dev launcher; reused, remote, or mock OpenCode servers keep their own logging configuration. Treat DEBUG output as sensitive local troubleshooting data because it may include request or provider details.
  • Provider error enrichment: if OpenCode reports only Provider returned error, LoopTroop scans the newest local OpenCode logs for the same session and records the exact sanitized provider cause when available. By default it looks in ~/.local/share/opencode/log/; set LOOPTROOP_OPENCODE_LOG_DIR when reusing an external OpenCode server whose logs live elsewhere.
  • Ephemeral auth: if OPENCODE_SERVER_PASSWORD is not set and a new local OpenCode server is about to start, npm run dev generates a random credential and sets OPENCODE_SERVER_USERNAME to opencode. This credential is propagated automatically to all child processes — backend and watcher — for the duration of the session.
  • Ephemeral API token: if LOOPTROOP_API_TOKEN is not set, npm run dev generates one for the backend and Vite dev proxy so local same-origin /api/* calls are protected without embedding the token in the frontend bundle.

Normal npm run dev is verify-only with respect to your dependencies: it never rewrites package.json, the lockfile, or a globally installed CLI. Dependency sync, npm audit remediation, and the OpenCode CLI upgrade are opt-in through LOOPTROOP_DEV_MAINTENANCE=1, or run explicitly with npm run deps:sync, npm run audit:remediate, and npm run opencode:upgrade. When opted in, that expensive networked maintenance work is daily-gated through tmp/dev-maintenance-state.json: each task runs on the first local dev start of the day, then runs again only if its relevant inputs change later that day.

"Verify-only" is scoped to dependencies, and preflight still performs three actions on your machine:

  • It runs npm ci when the installed tree has drifted from the lockfile. This is deterministic and lockfile-driven, so it installs exactly the pinned versions and introduces nothing new.
  • It terminates stale LoopTroop-owned dev processes from a previous session in this repository, leaving unrelated processes alone.
  • It reclaims the configured ports when they are held by those stale processes, and refuses to touch a port owned by anything else.

The last two exist so a crashed session does not block the next start. They apply only to processes this repository launched.

Audit failure handling distinguishes external availability from local integrity. Registry timeouts, connection errors, rate limits, service errors, and malformed audit responses are retried once and then reported as deferred without blocking normal startup. Because a deferred audit is not recorded as successful, the next eligible startup retries it. Local failures such as an unreadable lockfile, an invalid staged lockfile, or a failed dependency application remain startup-blocking. The standalone npm run audit:remediate command remains strict and exits unsuccessfully for either category so explicit maintenance and automation can detect incomplete work.

The 7-day release delay applies to direct npm package updates selected by dependency sync and to all npm package versions proposed by audit remediation. Before changing the live checkout, LoopTroop resolves proposed package and lock files in a temporary directory, then validates the result with npm ci --dry-run under npm's normal peer-dependency rules. Incompatible direct releases are held while compatible candidates can still proceed; related candidates are reconsidered together so a supporting package can unlock a previously incompatible update. If npm rejects a registry-hosted tarball as a remote URL during a direct-update preview, LoopTroop holds only the triggering direct update and retries it on the next daily check; it never loosens npm's remote-package policy. A rejected URL from any host other than the configured npm registry remains an error. Audit remediation is all-or-nothing: if npm rejects the proposed graph, or proposes any package version that is too fresh or whose publish time cannot be verified, LoopTroop holds the entire npm audit fix attempt. Every held-package detail states its specific cause: an incomplete 7-day release-safety period with the exact eligibility timestamp, unavailable npm metadata, a non-comparable version, an incompatible peer dependency with npm's exact constraint, or a registry-tarball policy hold. Accepted proposals are applied with npm ci; if that fails, the previous package files and dependency graph are restored. Automatic maintenance never bypasses npm conflicts with --force or --legacy-peer-deps. OpenCode is exempt only from the release-age delay: the local OpenCode CLI and direct @opencode-ai/sdk package update immediately when their normal maintenance path runs, while npm peer compatibility remains mandatory.

4. Maintenance Commands

Run the individual maintenance steps directly when you need tighter control:

bash
npm run deps:sync
npm run audit:remediate
npm run opencode:upgrade

Use one-run startup flags when you want to change npm run dev behavior:

bash
LOOPTROOP_DEV_MAINTENANCE=1 npm run dev
LOOPTROOP_DEV_MAINTENANCE=1 LOOPTROOP_DEV_SKIP_DEPS=1 npm run dev
LOOPTROOP_DEV_MAINTENANCE=1 LOOPTROOP_DEV_SKIP_OPENCODE_UPGRADE=1 npm run dev
LOOPTROOP_DEV_MAINTENANCE=1 LOOPTROOP_DEV_FORCE_MAINTENANCE=1 npm run dev
npm run dev --lan
npm run dev --opencode-logs=all

These commands update the same maintenance timestamps used by opted-in startup gating. deps:sync and audit:remediate still respect LOOPTROOP_DEV_SKIP_DEPS=1; opencode:upgrade still respects LOOPTROOP_DEV_SKIP_OPENCODE_UPGRADE=1. The LOOPTROOP_DEV_SKIP_* and LOOPTROOP_DEV_FORCE_MAINTENANCE flags only affect npm run dev when maintenance is opted in, since it is otherwise skipped entirely.

Scheduled Dependency Updates

Routine dependency updates are handled by Renovate rather than by local tooling, so the same policy applies whether or not any contributor happens to start the app. The configuration lives in renovate.json and is validated in CI, because an invalid rule is ignored silently at runtime rather than reported.

PolicySetting
ScheduleGrouped pull requests, Mondays before 06:00
Release maturity7 days before a version is proposed
Security advisories2 days, raised outside the weekly schedule
Dev dependenciesPatch and minor grouped, auto-merged once CI is green
Runtime dependenciesGrouped, always reviewed by hand
Major updatesOne pull request each, require dashboard approval
Lockfile refreshMonthly
GitHub ActionsPinned to commit SHAs and updated by Renovate

Dependencies with additional constraints:

  • drizzle-orm and drizzle-kit move together on the rc tag and stay exact-pinned. A global install re-resolves ranges on the user's machine and ignores the lockfile, so a loose range would ship an untested release candidate.
  • @opencode-ai/sdk waits 30 days. OpenCode is slated for replacement, so there is no reason to adopt its releases early; update the documented minimum version in the same pull request.
  • @types/node is held below the next major so it cannot drift ahead of the supported runtime and hide use of newer APIs.
  • tailwindcss and @tailwindcss/vite move together because the Vite integration must match the application stylesheet compiler.

npm audit runs in CI as a report only and never applies fixes automatically; remediation is a reviewed change.

5. Scripts Reference

Run any of these with npm run <name>. This is the subset worth knowing, not the full list — package.json currently declares far more, mostly the verify:*, build:* and release:* families that the release pipeline drives. npm run with no arguments prints all of them.

Development Stack

ScriptPurpose
devFull stack: frontend, backend, OpenCode watcher, and dev preflight. Standard start command. In-app documentation links point at the hosted docs site.
dev:appFrontend and backend only — no OpenCode watcher. Use when OpenCode is already running externally. Note: this bypasses the predev preflight (the predev hook only runs for dev), so dependency sync, npm audit, OpenCode upgrade, port-conflict cleanup, and the auto-generated OPENCODE_SERVER_PASSWORD / LOOPTROOP_API_TOKEN are skipped — set those yourself when needed.
dev:frontendVite dev server only.
dev:backendBackend Hono API server only.
dev:opencodeOpenCode watcher only.

Build And Preview

ScriptPurpose
buildType-check, then build the client bundle and the server (tsc -b && npm run build:client && npm run build:server). The server half is what makes the daemon and the standalone executable possible.
previewServe the last production build locally for inspection.

The frontend dev server pre-optimizes its complete declared browser dependency set before serving the app and disables browser storage of dev resources. This makes a process restart safe even when the browser restores a previously open LoopTroop tab: the restored document cannot retain an old React dependency graph while a lazy ticket workspace loads from the new process. The dependency policy is checked against production imports in the localized Vite configuration test.

Operational Tools

ScriptPurpose
predevAutomatic dev preflight hook that runs before npm run dev. Usually invoked through npm run dev, not by hand.
verify:publishedInstall a published release from its real feed using the documented command, start it, check the health endpoint, and remove it again. Needs network access and a version that is actually published — -- --channel npm --version X.Y.Z, or -- --plan --tier weekly to list the legs without running any. Normally driven by the Published install smoke workflow rather than by hand.
deps:syncPreview direct dependency updates with npm peer resolution, apply compatible releases with npm ci, hold conflicts, then refresh the daily-maintenance stamp.
audit:remediatePreview the gated npm audit remediation in isolation, hold incompatible proposals, and apply accepted lockfiles with npm ci.
opencode:upgradeRun only the OpenCode CLI upgrade step, then refresh the daily-maintenance stamp.
diagnose:stallGenerate a runtime diagnostics report under tmp/diagnostics/.

Tests And Code Quality

ScriptPurpose
testRun all test projects once and exit.
test:clientClient tests only (client-dom and client-node projects).
test:serverServer tests only (server-pure and server-integration projects).
test:watchRun all tests in watch mode. Useful during active development.
typecheckType-check the full project with tsc --noEmit.
lintLint the full project with ESLint.

vitest.config.ts defines four test projects:

  • client-dom — React component tests that require a JSDOM environment
  • client-node — client-side logic tests that do not need a DOM
  • server-pure — server unit tests with no I/O or database
  • server-integration — server integration tests running against a real local SQLite instance

Run test:client and test:server separately when you only want to validate one layer. Run test to validate both together.

Database Schema Tools

ScriptPurpose
db:generateGenerate app DB migration artifacts for external tooling review. Alias for db:generate:app; normal app schema changes still need server/db/schema.ts and runtime bootstrap updates in server/db/init.ts.
db:generate:appGenerate app DB migration artifacts from the configured app database target. Verify output against server/db/schema.ts before committing.
db:generate:projectGenerate project DB migration artifacts from LOOPTROOP_PROJECT_DB_PATH.
db:pushApp DB push command retained for ad-hoc local experiments only; do not use as the normal app schema-change workflow.
db:push:appSame as db:push; avoid for normal app schema changes because runtime bootstrap owns app DB creation/evolution.
db:push:projectPush schema changes directly to the project database target from LOOPTROOP_PROJECT_DB_PATH.

The app database is runtime-bootstrapped by server/db/init.ts. The committed migration directory is not the source of truth for live app startup. Project DB work should use the explicit project scripts.

6. Environment Variables

VariablePurpose
LOOPTROOP_FRONTEND_PORTOverride frontend port; also drives the default frontend origin when LOOPTROOP_FRONTEND_ORIGIN is unset
LOOPTROOP_FRONTEND_ORIGINOverride full frontend origin URL, for example http://my-server:5173; a valid explicit origin takes precedence over LOOPTROOP_FRONTEND_PORT, while an invalid value falls back to the default origin
LOOPTROOP_BACKEND_HOSTBackend bind host; defaults to 127.0.0.1
LOOPTROOP_BACKEND_PORTOverride backend port
LOOPTROOP_ALLOW_REMOTE_API=1Required before binding the backend to a non-loopback host; remote binds still require LOOPTROOP_API_TOKEN
LOOPTROOP_ALLOW_UNAUTHENTICATED=1Permit unauthenticated /api/* access only when no LOOPTROOP_API_TOKEN is configured; intended for local-only troubleshooting, never for use together with LOOPTROOP_ALLOW_REMOTE_API=1
LOOPTROOP_API_TOKENOptional token required by /api/*; npm run dev generates an ephemeral value when unset and the Vite dev proxy forwards it server-side
LOOPTROOP_TRUST_PROXY=1Trust x-forwarded-for / x-real-ip for rate-limit buckets; leave unset unless a trusted proxy owns those headers
LOOPTROOP_ENABLE_DEV_EVENT=1Enable the development-only ticket event injection route when paired with LOOPTROOP_DEV_EVENT_TOKEN
LOOPTROOP_DEV_EVENT_TOKENRequired secret for the dev-event route when it is enabled
LOOPTROOP_DOCS_ORIGINOverride the external documentation origin, for example a hosted preview deployment; defaults to https://www.looptroop.ovh
LOOPTROOP_DEV_HOSTDirect watcher fallback for LAN sharing; set to 1, true, 0.0.0.0, or a specific host/IP when not launching through npm run dev --lan
LOOPTROOP_OPENCODE_BASE_URLPoint LoopTroop at a specific OpenCode server
LOOPTROOP_CONFIG_DIROverride the app config directory
LOOPTROOP_APP_DB_PATHOverride the app database path directly
LOOPTROOP_PROJECT_DB_PATHProject database target for explicit Drizzle project DB commands
LOOPTROOP_DEV_MAINTENANCE=1Opt in to the daily dependency sync, npm audit remediation and OpenCode CLI upgrade during npm run dev; these are skipped by default because they rewrite package.json, the lockfile, or a globally installed CLI
LOOPTROOP_DEV_SKIP_DEPS=1Skip automatic dependency sync and audit remediation during npm run dev
LOOPTROOP_DEV_SKIP_OPENCODE_UPGRADE=1Skip the automatic local OpenCode CLI upgrade during npm run dev
LOOPTROOP_DEV_FORCE_MAINTENANCE=1Bypass the once-per-day maintenance gate and force all startup maintenance checks now
LOOPTROOP_OPENCODE_MODESet to mock to use the mock adapter instead of the real SDK adapter
LOOPTROOP_OPENCODE_PERMISSION_MODESet to inherit to skip setting OPENCODE_PERMISSION='"allow"' when npm run dev starts a managed OpenCode server; by default LoopTroop sets permissive mode automatically for local trusted sessions
LOOPTROOP_OPENCODE_LOGS=allDirect watcher fallback for npm run dev:opencode; starts a managed OpenCode server with --print-logs --log-level DEBUG when the watcher actually launches OpenCode
LOOPTROOP_OPENCODE_LOG_DIROptional OpenCode log directory used to enrich generic provider errors from an external or nonstandard OpenCode server; default lookup is ~/.local/share/opencode/log/
CHOKIDAR_USEPOLLINGGoverns both the frontend (Vite) and backend file watchers. Leave unset for auto-detection (native watching everywhere except WSL on a Windows-mounted drive). Set to 1 to force polling or 0 to force native watching
OPENCODE_SERVER_USERNAMEBasic auth username for the local OpenCode dev server; defaults to opencode when OPENCODE_SERVER_PASSWORD is also set
OPENCODE_SERVER_PASSWORDBasic auth password for the local OpenCode dev server; auto-generated as an ephemeral random credential by npm run dev if not set and a new local OpenCode server is about to start

Default local service addresses:

ServiceAddress
Frontendhttp://localhost:5173
Backendhttp://127.0.0.1:3000
Docshttps://www.looptroop.ovh/docs/ (hosted externally)
OpenCodehttp://127.0.0.1:4096

Default port resolution and origin building are implemented in shared/appConfig.ts, which validates environment variables and provides fallback defaults for the local application services plus the external documentation origin.

When LOOPTROOP_FRONTEND_ORIGIN is not explicitly set, LoopTroop derives the frontend origin from LOOPTROOP_FRONTEND_PORT, defaulting to http://localhost:5173. If LOOPTROOP_FRONTEND_ORIGIN is set but cannot be parsed as a URL origin, LoopTroop ignores it and falls back to that derived default.

LoopTroop accepts API tokens through either x-looptroop-token or Authorization: Bearer <token>.

IMPORTANT

Query-string credentials are not accepted, including on /api/stream. In the development stack, the Vite proxy injects LOOPTROOP_API_TOKEN as a header server-side, so native browser EventSource connections do not need access to the token. An installed browser instead sends its same-origin session cookie. An installed daemon's bearer token is the one it minted into daemon.json, not LOOPTROOP_API_TOKEN — see the API Reference for both models side by side.

Useful Health Endpoints

EndpointPurpose
GET /api/healthBackend availability, timestamp, and uptime
GET /api/health/opencodeOpenCode availability, version, and currently visible model list
GET /api/health/startupStartup storage/runtime snapshot used by the UI restore popup and mounted-drive warning surfaces
POST /api/health/startup/restore-notice/dismissPersist dismissal of the one-time startup restore popup

7. API Rate Limits

The backend applies a global per-client rate limit to API routes. Read requests, normal write actions, and UI-state autosaves use separate buckets so frequent draft saves do not exhaust the workflow-action budget. Defaults are 200 reads/minute, 120 normal writes/minute, and 300 autosaves/minute per client. The lightweight GET /api/health liveness probe is exempt from the read bucket so the UI can distinguish rate limiting from an unreachable backend; authentication still applies. If a client exceeds another limit, the API returns 429 with a Retry-After response header in seconds. Wait for that interval before retrying requests or refreshing aggressively.

Forwarded client IP headers are ignored unless LOOPTROOP_TRUST_PROXY=1 is set. This keeps local clients from bypassing limits by spoofing x-forwarded-for.

8. Project Git Hygiene

If .looptroop was already tracked before the project was attached, ticket startup is blocked with INIT_LOOPTROOP_TRACKED. This prevents nested or stale LoopTroop worktree data from being checked out into every new ticket worktree.

Clean that repository from the attached project root:

bash
git rm --cached -r .looptroop
git commit -m "Stop tracking LoopTroop runtime data"

This removes LoopTroop runtime paths from the Git index without deleting the local runtime files from disk.

After cleanup, git status --short .looptroop should not show tracked .looptroop entries. Runtime files may still exist locally, but they should be ignored according to the project's saved policy unless Nowhere was chosen. Ticket worktree artifacts under .ticket/** are likewise excluded from future bead commits; they remain available to LoopTroop but are not intended for target repository branches.

Other ticket initialization errors from the Git hygiene check:

  • INIT_LOOPTROOP_EXCLUDE_FAILED — LoopTroop could not apply the project's saved .looptroop/ and .ticket/ ignore policy. Check that the selected .gitignore or Git exclude destination is writable.
  • INIT_LOOPTROOP_TRACKED_CHECK_FAILED — The git ls-files check itself failed. Verify that the attached project path is a valid, accessible Git repository.

9. Worktree Disk Cleanup

Over time .looptroop/worktrees/ can grow large as completed and canceled tickets leave behind code checkouts, execution logs, and generated file artifacts.

Use the UI cleanup flow:

  1. Open Settings -> Projects and click Edit on the project you want to clean up.
  2. Click Free Disk Space... at the bottom-left, next to Delete Project.
  3. Click Calculate Size to see how much space can be freed.
  4. Click Delete Worktrees to remove worktrees for completed and canceled tickets.

Deleted: temporary directories at .looptroop/worktrees/<ticket>/ for tickets in the Completed or Canceled column, including code checkouts, execution logs, and AI-generated file artifacts.

LoopTroop restores owner removal permissions before deleting each eligible worktree. This handles project-agnostic read-only outputs such as dependency caches, downloaded toolchains, generated directories, and language package caches without requiring ecosystem-specific cleanup settings. Symlinks are removed without changing or traversing their external targets. Files owned by another operating-system user or protected by ACLs, immutable flags, or equivalent platform controls may still require the underlying ownership or protection to be corrected.

Preserved:

  • project source code and normal repository files
  • active, queued, and draft ticket worktrees
  • ticket records in the dashboard, including title, description, and status

10. Diagnostics

NOTE

diagnose:stall is a checkout-only tool. It lives in scripts/, which the published package does not ship, so an installed LoopTroop has no such command. From an installed copy, use looptroop doctor, looptroop status --json and looptroop logs -f — see Runtime Diagnostics.

If the UI feels slow, tickets disappear after refresh, or the app appears to stall, run the diagnostic command while npm run dev is still running:

bash
npm run diagnose:stall

The report is saved as tmp/diagnostics/runtime-stall-<timestamp>.log and includes endpoint latency, backend/frontend/OpenCode activity, trend-wide whole-system CPU/RSS/I/O consumers, pressure-stall metrics, SQLite/WAL state, attached project health, active sessions, Git responsiveness, and optional focused ticket runtime artifact sizing.

Useful options:

bash
npm run diagnose:stall -- --sample-ms 5000
npm run diagnose:stall -- --timeout-ms 8000
npm run diagnose:stall -- --trend-ms 0
npm run diagnose:stall -- --trend-ms 120000 --trend-interval-ms 1000
npm run diagnose:stall -- --ticket-path /path/to/worktree/.ticket

For the full diagnostics guide, including the runtime report plus blocked-error and structured-retry surfaces, see Runtime Diagnostics.

11. OpenCode Reachability

Symptoms:

  • the model list in the UI is empty
  • ticket logs show connection errors
  • phases that need a model block before drafting, setup, or execution

Checks:

When using npm run dev, port resolution and basic auth are handled automatically. The checks below apply when OpenCode is still unreachable after startup or when running the backend outside of npm run dev.

NOTE

An installed daemon does not need step 1. It adopts a running OpenCode or starts and supervises one itself — see OpenCode is managed for you. Step 2's X-LoopTroop-Token is also the wrong credential there: an installed daemon mints its own into daemon.json.

  1. Ensure OpenCode is running: opencode serve.
  2. Ping the backend health endpoint: curl http://127.0.0.1:3000/api/health/opencode. If you configured LOOPTROOP_API_TOKEN, include -H "X-LoopTroop-Token: $LOOPTROOP_API_TOKEN".
  3. If OpenCode is on a non-default port, set LOOPTROOP_OPENCODE_BASE_URL, for example export LOOPTROOP_OPENCODE_BASE_URL=http://127.0.0.1:4097.
  4. If you started OpenCode outside of npm run dev, ensure OPENCODE_SERVER_PASSWORD and OPENCODE_SERVER_USERNAME match the values LoopTroop is using. A credential mismatch causes silently failed requests.
  5. If LoopTroop only records generic provider failures, inspect the newest files under ~/.local/share/opencode/log/ or point LOOPTROOP_OPENCODE_LOG_DIR at the external server's log directory so LoopTroop can enrich those errors.

12. Watcher and WSL Performance Notes

Both the frontend (Vite) and backend watchers prefer native file watching on normal local filesystems — Linux (including a remote VPS), macOS, and native Windows all use fast native OS file-system events by default. Polling is only enabled automatically when it is genuinely required: a WSL runtime whose workspace lives on a Windows-mounted drive such as /mnt/c/..., where native watching is unreliable.

NOTE

Earlier versions forced polling for the frontend on every platform, which wasted CPU and added refresh latency on native Linux/macOS/Windows (most noticeable on remote hosts). Both watchers now share a single OS-agnostic decision (resolveWatchPollingDecision() in shared/wslPerformance.ts) so native watching is used everywhere unless polling is actually needed.

If your environment still misses file changes, force polling for the run (applies to both watchers):

bash
CHOKIDAR_USEPOLLING=1 npm run dev

You can also force native watching off a mounted drive with CHOKIDAR_USEPOLLING=0; an explicit value always overrides the auto-detection in either direction.

Windows-Mounted Drive Warning (WSL Users Only)

If you run LoopTroop inside Windows Subsystem for Linux (WSL), ensure that your attached target projects — and, if you are working from a checkout, the checkout itself — reside on the native Linux file system (e.g., under /home/username/... or another path in \wsl$). The project half of this applies to an installed LoopTroop too.

WARNING

Avoid Windows-mounted drives (like /mnt/c/... or /mnt/d/...) in WSL.

Keeping attached projects — or a LoopTroop checkout — on Windows-mounted drives severely degrades disk I/O performance. This slows down Git operations, codebase scanning, and test execution. It also disables native file-watching, forcing a fallback to chokidar polling (CHOKIDAR_USEPOLLING=1). For optimal performance, always store your workspaces and repositories inside the Linux home directory.

The path detection logic is implemented in shared/wslPerformance.ts, which exports isWslWindowsMountPath() to identify Windows-mounted paths, resolveWatchPollingDecision() to choose native watching vs. polling for both the frontend and backend watchers, and buildWslAppMountedDriveWarning() / buildWslProjectMountedDriveWarning() to generate targeted performance warnings.

When LoopTroop detects these mounted-drive paths, it surfaces the warning in two places: the startup UI warns when the LoopTroop app itself lives on a Windows-mounted drive, and project attachment warns when the target repository is mounted there.

13. Audit Warnings

npm audit --omit=dev should be clean. A full npm audit can still report dev-only findings through transitive development tooling:

  • drizzle-kit stable still depends on deprecated @esbuild-kit/*, which brings an older esbuild. The upstream issue is tracked here: drizzle-team/drizzle-orm#3067.

Do not run npm audit fix --force as routine maintenance for these warnings. The current forced fix path proposes a breaking drizzle-kit downgrade and does not represent a safe application hardening change.

LoopTroop documentation for the current runtime.