Krellbot docs

Security

The client is open source. It runs on your computer. krellbot.dev does not receive your exchange key or secret through the approved onboarding flow. The wizard's credentialed POST sends an authenticated HTTPS permission request to the selected Kraken or Coinbase venue. The separate CLI keys add stores without a probe; keys check probes afterward.

Code signing and notarization — current status

The frozen binaries published by the install endpoint are SHA-256 pinned against a server-validated manifest. The server refuses a manifest whose URLs are not HTTPS-or-loopback, whose digests are not 64 lowercase hex chars, whose sizes are not positive bounded integers, whose filename does not match the URL basename, or whose entry does not match its slot. The shell and PowerShell installers verify the downloaded bytes against the pinned digest and refuse to extract anything whose member list contains an absolute path, a .. path segment, an escaping symlink, a Windows drive letter, or a UNC prefix.

That is the integrity story for this release. The binaries are not code-signed, notarized, or Authenticode-signed today:

The site endpoint is also release-gated: until a release artifact is published and the release manifest is wired up, the endpoint returns 503 installer disabled. That response is the expected state for any chip / OS combination without a published release yet.

Where your keys live

The Exchange wizard requires a native OS keychain — Apple Keychain, Windows Credential Manager, or Secret Service / libsecret on Linux. It refuses plaintext, null, fail, and fake backends. There is no automatic plaintext-file fallback: CLI keys add writes to its configured keyring, while the legacy engine can read a separately managed KRELLBOT_<VEN>_KEYFILE only when explicitly configured and accepted by the platform-specific file checks. The wizard never writes that file. krellbot doctor reports the detected backend and readiness.

Credential-source precedence when the engine loads a key is a complete KRELLBOT_<VEN>_KEY and KRELLBOT_<VEN>_SECRET environment pair, then the keyring, then an explicitly configured keyfile only if no keyring entry exists. A partial environment pair is an error. A wizard-stored key does not override environment credentials; the live gate still checks the key it loads.

The credential is never written to the journal, to a receipt, to an exception trace, or to the status page. krellbot doctor and krellbot status never print the API key, the API secret, or the license value.

Where the browser POST goes

What does leave your machine, and why

The local first-run wizard (four steps, conditional)

When this release ships a frozen binary for your chip / OS combination (503 on the install endpoint goes away), krellbot ui renders a four-step wizard in the browser at the loopback URL: Welcome → Security → Exchange → Next.

  1. Welcome. What the engine is, and what you approve in this install.
  2. Security. Read-only local trust posture (keychain backend, $KRELLBOT_HOME path and POSIX mode, loopback bind, the live_arm_ui_allowed = False rail, a fail-closed aggregate diagnostic).
  3. Exchange. One credential-bearing POST to the loopback dashboard for Kraken or Coinbase. The handler sends an authenticated HTTPS request to the chosen venue to verify trade-only permission and stores the pair in the native OS keychain.
  4. Next. Enter the dashboard on explicit POST.

The wizard is server-rendered and works without JavaScript. It runs the POST → 303 PRG pattern, so the page the browser lands on is credential-free. The form fields have autocomplete="off"; no localStorage / sessionStorage carry the secret; the bootstrap JSON carries only nonsecret metadata.

The per-venue status block on the wizard is HISTORICAL only — it reads the durable timestamp from the last credential POST, never a fresh request to Kraken or Coinbase. The status reads "last stored through wizard at <ts>; current key presence not checked" on a stored row, and "unknown; not currently verified" on a missing or invalid row. It NEVER says "currently connected", and CLI-side key rotation is not detectable from the page (the GET never reads a credential).

The keyring write is two slots (key + secret) and is not atomic. On a slot write failure the wizard fails closed — it does not show green — and attempts a best-effort restore of the prior pair. If rollback cannot be proven, the wizard displays an uncertainty warning; check the keyring and run krellbot doctor before trading. The browser's historical status does not prove current key presence.

Trade on. Withdraw off.

The wizard stores only a key whose permission probe confirms trade-only access. The separate CLI keys add does not probe during storage; run keys check before trading. When creating a key, set trade permission on and withdraw permission off. The live arm gate refuses a key whose check_key reports can_withdraw=True. Krellbot does not implement a withdrawal action.

What the dashboard is and isn't

krellbot ui binds to 127.0.0.1 only. There is no host argument that can widen the bind. A request with a Host header other than 127.0.0.1:<port> or localhost:<port> is 403 Forbidden and sets no cookie. The OS will not deliver a packet to loopback from another host.

The URL the CLI prints carries a 64-hex-char gate token from secrets.token_hex(32). That token is the only path that gets a session cookie. The session cookie's value is matched to the URL token with hmac.compare_digest, so a timing oracle cannot be used to guess it.

POSTs require three checks at once: the session cookie matches the URL token, the Origin header is http://127.0.0.1:<port> or http://localhost:<port>, and a CSRF form field matches the krellbot_csrf cookie. Any single failure is 403 and changes no state. Live arm from the page is 403 by construction — the typed LIVE confirmation is a CLI-only path.

The static assets contain no http://, no https://, and no protocol-relative // URLs. The repository enforces this with a grep that must come back empty.

What the live tick path will never do

The engine never calls CancelAllOrdersAfter. The source is grepped for the string and a guardrail test enforces it. The engine never lowers a stop; raise_stop only replaces the resting stop if the new stop is strictly above it. The engine never sells more base than this pack's journal says it filled — extra coins the user holds on the venue are not touched.

Secrets in the journal

The journal, the receipts, and the exception text never carry a real secret, a real key, or a real order body. Sanitize with *** at the boundary. If you ever see something different, file an issue.

Telemetry

Telemetry is off until you type y. Until then, no telemetry is sent; an exchange permission probe or a live trading action can still contact the selected venue. See Telemetry for the exact payload shape and what it does not carry.

Home directory mode

On POSIX, KRELLBOT_HOME is created with mode 0o700. krellbot doctor warns when it is not. The journal, armed-pack config, and paper state live under that home; native keychain data is managed separately by the OS.

← docs