Security and privacy

Local data

Hop stores state in the project under .hop/. Prompt text, source trees, commands, and check output are local unless the project or its filesystem is copied elsewhere, or the user explicitly pairs Hop with a forge account. SQLite data is not encrypted at rest.

.hop/ is excluded through .git/info/exclude, so ordinary Git operations do not publish it. Initialization refuses to hide a .hop directory that the project already tracks.

Optional legacy prompt sync

Prompt history remains local by default. The existing Gitea compatibility adapter can pair with a Hop-enabled server. hop auth login FORGE_URL starts a browser-based OAuth Authorization Code flow with PKCE and a temporary 127.0.0.1 callback. Hop requests Gitea's all scope and stores the access and rotating refresh credentials in the operating-system keychain. The non-secret forge selection is stored in the user's config directory; tokens are never written to .hop, Git, command output, or JSON output.

After pairing, Hop derives the repository owner and name from its configured Git remote and sends redacted portable prompt records to the same forge origin. The server derives account identity and private-repository access from that OAuth bearer; local payloads cannot select a user ID. Hop also uses the grant for its own same-forge Git fetch/push operations. SSH-form remotes are rewritten to HTTPS only for the individual Git invocation, and the token is passed outside the process arguments. Sync is private, idempotent, batched, and best-effort. Proposal, acceptance, and landing remain successful if the forge is offline, and later sync attempts resend records from SQLite.

hop auth logout removes the local keychain credential. It does not delete local prompt history or previously synced server records.

Credential redaction

Before persistence, Hop redacts high-confidence provider keys, contextual tokens/passwords, private keys, authorization headers, and credential-bearing URLs. The same sanitizer is applied to proposal summaries and recorded check commands/output.

Detection is defense in depth, not a guarantee. Use environment variables or a secret manager. Rotate any real credential pasted into any agent prompt even when Hop reports a redaction.

Installer and release integrity

Packaged installers download checksums.txt from the same published GitHub Release and verify the selected archive before extraction. Releases are created as drafts, after race tests, vetting, and cross-platform builds, then must be reviewed before publication.

For stronger provenance before general availability, the release owner should sign checksums.txt with an offline-controlled release key and publish the public key independently. Checksum signing is listed as a launch gate in the release checklist.

Release-machine trust

Release builds execute on a trusted maintainer machine, not GitHub Actions. Publication uses the maintainer's existing authenticated gh session and uploads a draft for review. Hop and its agents never create, rotate, list, or revoke provider account tokens.

Filesystem safety

Hop does not use reset --hard, force a ref, or rewrite visible files while synchronizing Git. It may fast-forward the recorded attached branch and atomically replace a proven projection-only real index after revalidating the visible accepted tree, ancestry, locks, operations, and durable Hop state. Ordinary nonignored visible-root edits can be captured when the active branch/index tree proves the materialized base. If the visible Hop tree is projected over a genuinely different stale branch tree, implicit capture is blocked: Hop cannot safely distinguish deliberate edits from an external stale checkout and will not infer mass changes or deletions. Synchronization also remains fail-closed for ignored destinations, staged/index state, and filesystem and ref races. If any condition is unprovable, Hop preserves the branch, index, and files and returns the exact blocking reason and safe action.

New checkpoints, proposals, captures, reconciliation results, remote compositions, lands, accepts, retries, and undos carry exact-tree authorization proofs. Before accepted state advances, Hop recomputes the changed-path manifest and verifies exact Git object IDs and modes. hop status reports accepted_provenance as verified, legacy_unverified, or invalid; hop doctor recomputes proofs and reports tampering or missing Git inputs.

Automatic push uses the existing Git transport for every host. For a legacy Gitea forge selected by hop auth login, Hop stores the OAuth grant only in the OS keychain and passes a repository-scoped authorization header to its Git subprocess via the environment. It never embeds the token in a remote URL or command argument, never persists it in Git configuration, disables terminal prompting, and leaves the configured remote unchanged.

hop repo create and hop forge api use the same grant for repository and Gitea API operations without exposing it to the agent. hop auth exec is an explicit compatibility boundary for trusted child tools that require a token environment variable. The child receives the credential for its lifetime, so agents must use it only for an authorized operation and must never print or persist the variable.

Hop's native Gitea collaboration commands embed the open-source Gitea command engine in-process. Before invoking it, Hop refreshes the keychain-backed OAuth grant and supplies it only through a temporary process environment. The prior environment is restored after the command. No Tea executable, Tea configuration, or second credential store is used.

Reporting a vulnerability

Before the public security contact is configured, disclose vulnerabilities privately to the repository owner rather than opening a public issue. Add a SECURITY.md with the final contact before the first public release.