diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..87518f8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,99 @@ +name: Bug report +description: Report a reproducible problem in Deckard (0.0.1-alpha). +title: "[bug]: " +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to file a bug. + + **Deckard is `0.0.1-alpha` — experimental, pre-1.0 software.** It is not production-ready, + has not had a third-party security audit, and must only be used with **testnet / throwaway + keys — never real funds or real mainnet keys.** + + > **Stop:** if this is a *security vulnerability* (anything that could expose a key, seed, + > passphrase, or signed transaction), do **not** open a public issue. Report it privately — + > see [`SECURITY.md`](https://github.com/hellno/deckard/blob/main/SECURITY.md). Public disclosure puts other users at risk. + + - type: checkboxes + id: acknowledgements + attributes: + label: Acknowledgements + description: Please confirm all of the following before filing. + options: + - label: "I am **NOT** reporting a security vulnerability (those go to `SECURITY.md`, privately)." + required: true + - label: "I am using **testnet / throwaway keys, not real funds**." + required: true + - label: "I searched existing issues and this is not a duplicate." + required: true + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear, concise description of the actual behavior. Include any error output or screenshots (redact any key/seed/address you would rather not share). + placeholder: When I ..., Deckard did ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What did you expect to happen? + description: A clear, concise description of the expected behavior. + placeholder: I expected Deckard to ... + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: The exact sequence to reliably reproduce the problem. + placeholder: | + 1. Run `just run` + 2. Complete onboarding with a throwaway seed + 3. Click '...' + 4. See error + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Operating system + options: + - macOS + - Linux + validations: + required: true + + - type: input + id: os-version + attributes: + label: OS version + description: e.g. "macOS 15.5 (Apple Silicon)" or "Ubuntu 24.04 / Fedora 40". + placeholder: macOS 15.5 (Apple Silicon) + validations: + required: true + + - type: input + id: app-version + attributes: + label: Deckard version + description: The current pre-release is `0.0.1-alpha`. If you built from a specific commit, paste the short SHA. + placeholder: 0.0.1-alpha (commit abc1234) + value: "0.0.1-alpha" + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant logs or context + description: Paste any relevant terminal output. **Never paste a seed phrase, private key, or passphrase** — they are not needed to triage a bug. + render: shell + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..96e221c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +blank_issues_enabled: false +contact_links: + - name: Report a security vulnerability (private) + url: https://github.com/hellno/deckard/blob/main/SECURITY.md + about: >- + Do NOT open a public issue for security problems. Deckard holds private keys, seed phrases, + and an encrypted keystore. Follow the private disclosure process in SECURITY.md. + - name: Questions & discussion + url: https://github.com/hellno/deckard/discussions + about: >- + Have a general question, idea, or want to talk through an approach? Use GitHub Discussions + instead of opening an issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..912a047 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,49 @@ +name: Feature request +description: Suggest an idea or improvement for Deckard. +title: "[feature]: " +labels: ["enhancement", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for the idea. Deckard is `0.0.1-alpha` — experimental, pre-1.0 software — so the + roadmap is still forming. A clear problem statement helps far more than a fully-specified + solution. + + > Not a feature request? For security reports see [`SECURITY.md`](https://github.com/hellno/deckard/blob/main/SECURITY.md); + > for open-ended questions use [Discussions](https://github.com/hellno/deckard/discussions). + + - type: textarea + id: problem + attributes: + label: Problem / motivation + description: What problem are you trying to solve? What is the use case, and who is affected? Describe the situation, not just the fix. + placeholder: I'm always frustrated when ... / Today there is no way to ... + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: What would you like to happen? Describe the behavior or capability you have in mind. + placeholder: Deckard could ... + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: What other approaches or workarounds have you considered, and why are they insufficient? + placeholder: I considered ..., but ... + validations: + required: false + + - type: textarea + id: context + attributes: + label: Additional context + description: Anything else — mockups, links, prior art, or how this fits the self-custodial / privacy-by-default direction. + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..7e2500e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,52 @@ + + +## Summary + + + +## Linked issue + + + +Closes # + +## Definition of Done + +All of the following must hold before this PR can merge. **Paste the command output as evidence — +do not check a box you have not verified.** + +- [ ] `cargo fmt --all --check` is clean +- [ ] `just check` is green — clippy `-D warnings` on **both** the default config **and** `--features tray` +- [ ] `cargo test --workspace` is green +- [ ] No new or changed dependencies in `Cargo.toml` / `Cargo.lock` (unless explicitly approved in this PR; + the git GPUI stack is bumped only via `just bump-gpui`, never hand-edited) +- [ ] Any visual/UI change follows `DESIGN.md` (amber = human, cyan = agent; sidebar/contextual-views IA; + clear-signing / seed-reveal trust affordances) +- [ ] Secrets (seed / key / passphrase) stay in `Zeroizing` and are never logged or `Debug`-printed + +
+Evidence (paste command output here) + +```text +$ cargo fmt --all --check +# (output) + +$ just check +# (output) + +$ cargo test --workspace +# (output) +``` + +
+ +## Notes for reviewers + + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..345a1e0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,116 @@ +# Changelog + +All notable changes to **Deckard** — a native, self-custodial Ethereum wallet +(GPUI + Rust; macOS + Linux) — are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +> **Pre-1.0 alpha.** Deckard is experimental software under active development. While +> pre-1.0, the public surface (APIs, the frozen wire contract, on-disk keystore format, +> CLI/commands) may change between releases without a major-version bump, per semver's +> `0.y.z` rules. **Do not use Deckard with real funds or real mainnet keys** — testnet +> or throwaway keys only. No third-party security audit has been performed. + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [0.0.1-alpha] - 2026-06-10 + +First tagged alpha. This is a security-sensitive, experimental wallet: the core +trust mechanisms are built and de-risked, but the end-to-end demo flow is not yet +wired together. See [`STATUS.md`](STATUS.md) for the authoritative, beat-by-beat +state of the build, and [`DESIGN.md`](DESIGN.md) for the design system. + +Licensed under **AGPL-3.0-or-later** (see [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE)). +Forked from the [`deck`](https://github.com/hellno/deck) GPUI starter (0BSD, which +permits relicensing), now its own project. + +Workspace layout (a virtual Cargo workspace, all crates under `crates/`): + +- **`deckard-app`** — the GPUI application (binary `deckard`). +- **`deckard-core`** — the headless engine: provider / verified reads, balances, + HD keys, keystore, and the key-less shield builder. +- **`deckard-contract`** — the frozen wire contract (`Intent` / `Decision` / + `Policy` / RPC / `ReadStatus`). +- **`deckard-signerd`** — the process-isolated signer daemon. + +### Added + +- **Encrypted BIP-39 keystore + onboarding.** Self-custodial seed-vault flow that + generates or imports a BIP-39 seed and stores it encrypted at rest. +- **Live on-chain balances.** Real balances over an `alloy` provider, batched through + Multicall3. +- **Helios-verified reads.** Reads are verified against a Helios light client — **no + third-party RPC is trusted by default** — with a `ReadStatus` badge surfaced in the + app. +- **Receive.** Your address plus a QR code. +- **Command palette.** Keyboard-first navigation across the app. +- **Design system (`DESIGN.md`).** The amber-on-near-black visual language, including + the two-signal actor model (amber = human, cyan = agent), wired into onboarding, + portfolio, receive, palette, and settings. +- **Process-isolated signer daemon (`deckard-signerd`).** A separate process holds the + key and gates every write over a Unix domain socket, with a policy gate, a + `propose → Decision → execute` flow, and a STOP control that zeroizes the in-memory + secret. The app talks to it through a socket signer client. +- **The shield hero (auto-private via Railgun).** The auto-shield mechanism — the + `deckard-core` key-less shield builder plus daemon broadcast — is **wired and + black-box tested on an anvil fork.** (See *Known limitations* for what is not yet + reachable from the app or an agent.) +- **Agentic-engineering policy.** A workspace-wide lint / CI / supply-chain policy + that denies `todo!`, `dbg!`, and ignored `Result`s, with a documented Definition of + Done (see *Notes*). + +### Security + +- **Process isolation.** The signing key lives only inside `deckard-signerd`, a + separate process reached over a Unix domain socket; the GUI never holds it. +- **Verified reads by default.** Chain reads are checked against a Helios light client + rather than trusting any single third-party RPC. +- **Keystore at rest.** The seed is sealed in an Argon2id + XChaCha20-Poly1305 + envelope. +- **Secrets in `Zeroizing`.** Seeds, keys, and passphrases are held in zeroizing + buffers and are never logged or `Debug`-printed. +- **`#![forbid(unsafe_code)]` in `deckard-core`** (the trust core); the app crate sets + `unsafe_code = "deny"`. + +### Notes + +- **Definition of Done** (all must hold for a change to be considered done): + 1. `cargo fmt --all --check` is clean. + 2. `just check` is green — clippy `-D warnings` on **both** the default and + `--features tray` configurations. + 3. `cargo test --workspace` is green. + 4. No new or changed dependencies in `Cargo.toml` / `Cargo.lock` unless explicitly + approved. +- **Commands:** `just run` (build signerd + run the app), `just core` (the fast engine + inner loop), `just check` (lint both configs), `cargo test --workspace`, and + `just bundle` (build a macOS `.app`). The toolchain is pinned in + `rust-toolchain.toml`. + +### Known limitations + +This is an **alpha**. It is **not** production-ready and **not** safe for real funds — +use testnet or throwaway keys only, and never a real mainnet key. No third-party +security audit has been done. + +- **Send UI is gated** — marked "next release"; not available in this build. +- **Swap is a TODO** — the button is disabled. +- **No agent / MCP surface** — `deckard-mcp` is **not built**; only the wire contract + and the daemon socket exist for it to build on. +- **Receive-watcher auto-detect is a TODO** — inbound funds are not auto-detected and + the shield is **not** yet triggerable from the app or an agent. The shield hero is + reachable only from the test/manual path, not on-screen. +- **Some tests are `#[ignore]`** — the network-dependent suites (notably + `signerd/shield_e2e`) need a local `anvil` plus an archive RPC and are not run by + default `cargo test`; the default-on `anvil_e2e` silently skips if `anvil` is + missing, and some unit tests exercise mocked transports / a fake recording daemon + rather than a live chain. See the test caveats in [`STATUS.md`](STATUS.md). + +[Unreleased]: https://github.com/hellno/deckard/compare/v0.0.1-alpha...HEAD +[0.0.1-alpha]: https://github.com/hellno/deckard/releases/tag/v0.0.1-alpha diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..884ef0a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,85 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at holgerniessner@me.com. All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..86b4415 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,163 @@ +# Contributing to Deckard + +Thanks for your interest in Deckard — a native, self-custodial Ethereum wallet built in +Rust + GPUI for macOS and Linux. Contributions are welcome, and this guide should get you +from clone to your first green PR. + +## Read this first: alpha, and security-sensitive + +Deckard is **`0.0.1-alpha` — experimental, pre-1.0 software.** It is **not production-ready**. + +This wallet holds private keys, BIP-39 seed phrases, and an encrypted keystore. That makes +every change here **security-sensitive**. Treat key material with care: + +- **Testnet and throwaway keys only.** Do **not** run Deckard with real funds or real mainnet + keys, and never paste a real seed phrase into it. No third-party security audit has been done yet. +- **Never log, `Debug`-print, or otherwise expose a seed, key, or passphrase.** Secrets live in + `Zeroizing` and stay there. +- If you find a vulnerability, **do not open a public issue** — see + [Reporting security issues](#reporting-security-issues) below. + +## Getting started + +### 1. Clone + +```sh +git clone https://github.com/hellno/deckard +cd deckard +``` + +### 2. Toolchain + +The Rust toolchain is **pinned in `rust-toolchain.toml`** (currently `1.95.0`, in lockstep with +Zed's GPUI). With `rustup` installed, the pinned toolchain (plus `rustfmt` and `clippy`) is selected +automatically when you build inside the repo — you don't need to switch channels by hand. + +### 3. Install `just` + +The task runner is [`just`](https://github.com/casey/just): + +```sh +brew install just # or: cargo install just +``` + +Everything in the `justfile` is plain `cargo` + macOS built-ins, so you can also run the underlying +commands by hand if you prefer. + +### 4. Crate layout + +Deckard is a **virtual Cargo workspace** — the root carries no `[package]`; all four crates live +under `crates/`: + +| Crate | Role | +|---|---| +| `deckard-app` | The GPUI desktop app (the view layer / shell; binary `deckard`). | +| `deckard-core` | The headless engine: Ethereum provider + verified reads, balances, HD keys, encrypted keystore, key-less shield builder. **No GPUI dependency; fully unit-testable** — most logic belongs here, not in the app. | +| `deckard-contract` | The frozen wire contract (`Intent` / `Decision` / `Policy` / `RPC` / `ReadStatus`). | +| `deckard-signerd` | The process-isolated signer daemon — owns the key and gates writes over a Unix-domain socket. | + +### 5. The inner loop + +- **`just core`** — the fast inner loop. Clippy + tests for the GPUI-free engine (`deckard-core`) + **without building the GPUI app.** The heavy verified-reads / shield deps compile once, then it's + quick. Reach for this while iterating on the keystore, provider, balances, or shield builder. +- **`just check`** — the full lint pass (clippy `-D warnings` across the whole workspace **and** the + app's `--features tray` config). Required for any UI work, and part of the Definition of Done below. +- **`just run`** — builds `deckard-signerd`, then runs the app (the signerd binary is spawned as a + sibling). This is the command you'll use most for UI work. +- **`cargo test --workspace`** — runs the full test suite. +- **`just bundle`** — builds a distributable macOS `Deckard.app`. + +> The fast `just core` loop is for iterating — the **full Definition of Done still applies before +> you're done.** + +## Definition of Done + +All of these must hold before a change is complete (reproduced verbatim from `AGENTS.md`): + +1. `cargo fmt --all --check` clean +2. `just check` green (both feature configs) +3. `cargo test --workspace` green +4. No new/changed dependencies in `Cargo.toml` or `Cargo.lock` unless explicitly approved + +**Never report a task complete while any of these is red. Paste the command output as evidence — +don't claim done while red.** + +## Code constraints + +The full rationale (what we enforce and *why*, plus the deliberately-rejected rules) lives in +[`docs/AGENTIC-ENGINEERING.md`](docs/AGENTIC-ENGINEERING.md). The summary: + +**In `deckard-core`** (the trust core), enforced via crate-level `#![deny(...)]` — clippy fails the +build: **no `.unwrap()` / `.expect()` / `panic!` / raw slice indexing in non-test code.** Propagate +errors with `Result` / `?`, and parse untrusted bytes through the bounded `Reader` in `keystore.rs`. +The app crate may `unwrap` infallible GPUI handles; the engine must not. A genuinely-unrecoverable +boundary uses a scoped `#[allow]` + a `// reason` comment (see `eth.rs`) — never a bare `unwrap`. + +**Workspace-wide**, enforced by `[workspace.lints]` + `clippy.toml` (CI fails the build): + +- `todo!` and `dbg!` are denied; ignored `Result`s (`unused_must_use`) are denied. +- `deckard-core` is `#![forbid(unsafe_code)]`; the app crate is `unsafe_code = "deny"` (a new + `unsafe` block needs a reviewed `// SAFETY:` comment + an explicit `#[allow]`). +- `std::mem::forget` / `core::mem::forget` and `rand::thread_rng` are denied — use `drop()` / + `OsRng`. + +**Always, every crate:** never log or `Debug`-print a seed, key, or passphrase. Secrets stay in +`Zeroizing`. + +## Design changes + +Before any visual or UI change, **read [`DESIGN.md`](DESIGN.md) first** — it is the source of truth +for fonts, color, spacing, the information architecture, and component states. A few load-bearing +rules: + +- **Ground every design decision in real reference screenshots** (Linear, Conductor, Splits, + Superhuman, Stripe), never in remembered descriptions — that's how the first drafts went wrong. +- **The two-signal actor model:** **amber = the human** ("where you are" / caution); **cyan = the + agent** (the machine actor). The two never mean the same thing. + +Don't deviate from `DESIGN.md` without explicit maintainer approval. + +## Workflow + +- **Branch off `main`**, do your work, and open a **PR back to `main`.** +- **Expect adversarial cross-model review.** Changes go through review by a second model (e.g. Codex + GPT-5.5 as a cross-model reviewer) — design for that scrutiny. +- **Keep dependencies frozen.** No new or changed `Cargo.toml` / `Cargo.lock` deps without explicit + approval. The git GPUI stack is bumped **only** via `just bump-gpui` — never hand-edit those pins. +- Satisfy the full **Definition of Done** before requesting review, and include the command output + as evidence. + +## Where to look + +| File | What it is | +|---|---| +| [`AGENTS.md`](AGENTS.md) / `CLAUDE.md` | Guidance for coding agents (and a fast human orientation). | +| [`STATUS.md`](STATUS.md) | The honest, live status of what's built vs. partial vs. TODO. **Treat it as ground truth.** | +| `specs/` and `docs/build/` | The specs and build notes. | +| [`docs/AGENTIC-ENGINEERING.md`](docs/AGENTIC-ENGINEERING.md) | The *why* behind the lint policy, CI gates, and code constraints. | +| [`DESIGN.md`](DESIGN.md) | The design system — required reading before any UI change. | + +### What works today (and what doesn't) + +Per `STATUS.md` — don't overstate maturity: + +- **Working:** encrypted BIP-39 keystore + onboarding; live on-chain balances (Multicall3); receive + (address + QR); the command palette; the amber-on-near-black design system; Helios-verified reads + (no third-party RPC trusted by default); the process-isolated signer daemon (`deckard-signerd`) + that holds the key and gates writes; the shield hero (auto-private via Railgun), which is **wired + and black-box tested on an anvil fork.** +- **Not done / partial:** the **Send** UI is gated ("next release"); **Swap** is TODO; the agent / + MCP surface (`deckard-mcp`) is **not built**; the receive-watcher auto-detect is TODO. Some tests + are `#[ignore]` (they need `anvil` + an archive RPC) — see the test caveats in `STATUS.md`. + +## Reporting security issues + +**Do not open a public issue for a security vulnerability.** Report it privately per +[`SECURITY.md`](SECURITY.md), or directly to the maintainer at **holgerniessner@me.com**. + +## License + +Deckard is licensed under **AGPL-3.0-or-later** (see [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE)). +It was forked from the [`deck`](https://github.com/hellno/deck) GPUI starter (0BSD, which permits +relicensing). By contributing, you agree your contributions are licensed under AGPL-3.0-or-later. diff --git a/Cargo.lock b/Cargo.lock index e1beee4..b9b60e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3884,7 +3884,7 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deckard-app" -version = "0.1.0" +version = "0.0.1-alpha" dependencies = [ "alloy-primitives", "alloy-signer-local 2.0.5", @@ -3910,7 +3910,7 @@ dependencies = [ [[package]] name = "deckard-contract" -version = "0.1.0" +version = "0.0.1-alpha" dependencies = [ "alloy-primitives", "ciborium", @@ -3920,7 +3920,7 @@ dependencies = [ [[package]] name = "deckard-core" -version = "0.1.0" +version = "0.0.1-alpha" dependencies = [ "alloy", "alloy-primitives", @@ -3944,7 +3944,7 @@ dependencies = [ [[package]] name = "deckard-signerd" -version = "0.1.0" +version = "0.0.1-alpha" dependencies = [ "alloy", "alloy-primitives", diff --git a/README.md b/README.md index a42940b..ffb2ab1 100644 --- a/README.md +++ b/README.md @@ -3,42 +3,118 @@ A fast, keyboard-first, **self-custodial Ethereum wallet** for people who live onchain. Native (macOS + Linux), trustless by construction, open source. +> ## ⚠️ v0.0.1-alpha — EXPERIMENTAL. NOT for production. +> This is **pre-1.0, experimental software** with **no third-party security audit**. +> **Do NOT use it with real funds or real mainnet keys.** Use **testnet / throwaway keys only.** +> It can lose your money or break at any time. You have been warned. + +![Deckard demo](docs/demo.gif) + +> _Draft demo from the current build: onboarding → verified split-balance → command palette → receive → shield-to-private. The final cut is swapped in once the in-flight UI polish lands ([`docs/RELEASING.md`](docs/RELEASING.md) §4)._ + > Forked from the [`deck`](https://github.com/hellno/deck) GPUI starter (0BSD, which permits > relicensing). Now its own project: Rust + [GPUI](https://www.gpui.rs/), licensed AGPL-3.0-or-later. +For the bigger picture — *why* Deckard exists and where it's going — read [the why](docs/launch-pitch.md). + ## Status **Live build status: [`STATUS.md`](STATUS.md) — the single source of truth** (demo beats, crates, risks). -Working today: encrypted BIP-39 keystore + onboarding, live on-chain balances (Multicall3), receive (QR), -command palette, and the amber-on-near-black design system (`DESIGN.md`). Reads are **Helios-verified** (no -third-party RPC trusted by default). A process-isolated signer daemon (`deckard-signerd`) holds the key and -gates every write. The **shield** hero (auto-private via Railgun) is wired + black-box tested on an anvil fork. -Next: receive-watcher, the agent (MCP) surface, Send/Swap UI. See `STATUS.md` for the beat-by-beat picture. +**Working today:** + +- Encrypted BIP-39 keystore + onboarding (Argon2id + XChaCha20-Poly1305 at rest; secrets in `Zeroizing`). +- Live on-chain balances (Multicall3). +- Receive (address + QR). +- Command palette. +- The amber-on-near-black design system (`DESIGN.md`). +- **Helios-verified reads** — no third-party RPC is trusted by default. +- A **process-isolated signer daemon** (`deckard-signerd`) over a Unix socket that holds the key and + gates every write. +- The **shield** hero (auto-private via [Railgun](https://railgun.org/)) is **wired + black-box tested + on an anvil fork**. -## Roadmap +**Not done yet (do not expect these to work):** -- **v0** — the wallet (above). -- **next** — live balances (alloy provider + Multicall3, Helios light client), Send/Swap - (alloy + CoW Swap), and the BIP-39 seed-backup flow. All on audited libraries. -- **v1** — the sovereign autopilot: policy-bounded automation over your own keys. +- **Send** UI is gated ("next release"). +- **Swap** is a TODO. +- The **agent / MCP surface** (`deckard-mcp`) is **not built**. +- The **receive-watcher** auto-detect is a TODO. +- Some tests are `#[ignore]` (need `anvil` + an archive RPC) — see the test caveats in `STATUS.md`. + +See `STATUS.md` for the beat-by-beat picture and the honest test caveats. ## Build & run ```sh -cargo run # debug build + run -just run # same, via the task runner -just bundle # build a macOS Deckard.app +just run # build the signer daemon, then build + run the app (debug) +just core # fast engine-only inner loop (clippy + tests for deckard-core, no GPUI build) +just check # lint both feature configs (clippy -D warnings: default AND --features tray) +cargo test --workspace # run the test suite +just bundle # build a macOS Deckard.app ``` -Requires a recent stable Rust toolchain. +The Rust toolchain is pinned in `rust-toolchain.toml`. Install [`just`](https://github.com/casey/just) +with `brew install just` (macOS) or `cargo install just` (any platform). Under the hood `just run`, +`just core`, `just check`, and `cargo test` are plain `cargo`, so you can run them by hand too. + +**Linux:** the app builds with `cargo`, but GPUI needs the same system libraries Zed does — a Vulkan +loader plus the X11/Wayland and font/clipboard dev packages. Install them per +[Zed's Linux build dependencies](https://github.com/zed-industries/zed/blob/main/docs/src/development/linux.md) +before `just run`. Note that `just bundle` / `just open` / `just icon` are **macOS-only** (they shell +out to `cargo-bundle` (osx), `open`, `sips`, and `iconutil`); on Linux use `cargo build` / `cargo run` +directly. + +**Definition of done** (all must hold before a change is finished): + +1. `cargo fmt --all --check` is clean. +2. `just check` is green (clippy `-D warnings` on **both** the default and `--features tray` configs). +3. `cargo test --workspace` is green. +4. No new or changed dependencies in `Cargo.toml` / `Cargo.lock` unless explicitly approved. + +### Crate layout + +Virtual Cargo workspace; all crates live under `crates/`: + +- **`deckard-app`** — the GPUI app (binary `deckard`). +- **`deckard-core`** — the headless engine: provider / verified-reads, balances, HD keys, keystore, and the + key-less shield builder. `#![forbid(unsafe_code)]`. +- **`deckard-contract`** — the frozen wire contract (`Intent` / `Decision` / `Policy` / `RPC` / `ReadStatus`). +- **`deckard-signerd`** — the signer daemon that holds the key and gates writes. + +## Security architecture + +Real, and already built: + +- **Process-isolated signer daemon** (`deckard-signerd`) over a Unix domain socket: it holds the key and + gates every write; the app and engine are key-less. +- **Helios light-client verified reads** — no third-party RPC is trusted by default. +- **Keystore at rest** = Argon2id key derivation + an XChaCha20-Poly1305 envelope; secrets stay in `Zeroizing`. +- `deckard-core` is `#![forbid(unsafe_code)]`; the workspace lint policy denies `todo!`, `dbg!`, and ignored + `Result`s. + +This is alpha software with **no external audit yet** — treat the above as design intent under active review, +not a guarantee. Report anything sensitive privately (see below). ## Design The visual + interaction system (typography, color, spacing, the command palette, and the -clear-signing/trust affordances) lives in [`DESIGN.md`](DESIGN.md) — the source of truth for any +clear-signing / trust affordances) lives in [`DESIGN.md`](DESIGN.md) — the source of truth for any UI work. +## Contributing + +This is **alpha software**, and money software at that — it should not be reviewed by one person. I'm +especially looking for a **security-minded co-maintainer** for the signer/policy/keystore surface. If +reviewing self-custody enforcement is your thing, please reach out. + +- [`CONTRIBUTING.md`](CONTRIBUTING.md) — how to build, test, and submit changes. +- [`SECURITY.md`](SECURITY.md) — how to report a vulnerability **privately** + (security contact: holgerniessner@me.com). +- [`CHANGELOG.md`](CHANGELOG.md) — what changed, release by release. + +Repo: . + ## License AGPL-3.0-or-later. See [`LICENSE`](LICENSE) and third-party attributions in [`NOTICE`](NOTICE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..39b423a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,198 @@ +# Security Policy + +Deckard is a native, self-custodial Ethereum wallet (GPUI + Rust; macOS + Linux). +Because it holds private keys, BIP-39 seed phrases, and an encrypted keystore, its +security posture matters more than almost anything else in the project. This document +states honestly what protects you, what does **not** yet, and how to report a problem +privately. + +--- + +## ⚠️ Alpha warning — read this first + +> **Deckard is `0.0.1-alpha`: EXPERIMENTAL, UNAUDITED, PRE-1.0 software.** +> +> **Do NOT store real funds in it. Do NOT use real mainnet keys or a seed phrase that +> controls real value.** Use **testnet keys or throwaway keys only.** +> +> No third-party security audit has been performed. This is not production-ready software. +> **Use it entirely at your own risk.** If you load a key that controls funds you care +> about, you may lose them. + +This is alpha software released for evaluation and development. Treat every wallet you +create or import in Deckard as disposable until a release explicitly says otherwise. + +--- + +## Supported versions + +Security fixes are **best-effort during the alpha** and target only the latest alpha build. +There is **no LTS guarantee and no backport guarantee before 1.0** — APIs, the on-disk +keystore format, and the security model may all change without notice. + +| Version | Supported | Notes | +| ------------- | ---------------------------------- | --------------------------------------------------------- | +| `0.0.1-alpha` | ✅ Best-effort security fixes only | Latest alpha. No LTS. Testnet / throwaway keys only. | +| `< 0.0.1` | ❌ Unsupported | Pre-release / development snapshots. | + +Always run the latest available build. Older alpha builds receive no fixes. + +--- + +## Reporting a vulnerability + +**Please report security issues privately. Do NOT open a public GitHub issue, pull +request, or discussion for a security vulnerability** — public disclosure before a fix is +available can put users' keys and funds at risk. + +Use **either or both** of these private channels: + +1. **GitHub private vulnerability reporting** (preferred) — go to the repository's + **Security** tab → **Report a vulnerability**: + +2. **Email** the maintainer directly: **holgerniessner@me.com** + (PGP available on request — ask in your first message if you want to encrypt details.) + +### What to include + +To help us triage and reproduce quickly, please send: + +- **Description** — what the issue is, in your own words. +- **Reproduction** — exact steps, environment (OS, build/commit), and any proof-of-concept. +- **Impact** — what an attacker can do (e.g. key/seed exposure, signing without consent, + bypassing the policy gate, forging a "verified" read, keystore downgrade/tamper). +- **Suggested fix or mitigation**, if you have one. + +### Our commitment + +- **Acknowledgement:** best-effort, typically **within ~72 hours** during the alpha. Deckard + is currently maintained by a single person, so please allow for time-zone and availability + delays. +- **Coordinated disclosure:** we will work with you on a fix and a disclosure timeline. We + ask that you give us a reasonable window to ship a fix before any public write-up, and we're + happy to credit you in the advisory (or keep you anonymous — your choice). +- **No bounty program** exists during the alpha. We deeply appreciate responsible reports + regardless. + +Please act in good faith: test only against your **own** wallets / testnet keys, never +against other users' funds, and don't run destructive, privacy-invasive, or +denial-of-service tests against shared infrastructure. + +--- + +## Security model — what actually protects you + +These mechanisms are built and exercised in tests (with the honest caveats noted in the +section below). The architecture is deliberately defense-in-depth around the key. + +- **Process-isolated signer daemon (`deckard-signerd`).** The private key lives in a + **separate daemon process**, not in the GPUI app. The daemon owns the key and **gates + every state-changing operation** (every write/signing request) behind an explicit + policy check, communicating with the app over a **Unix domain socket (UDS)**. The UI + cannot sign on its own — it can only *propose* an intent, which the daemon turns into a + `Decision` and may execute. On lock/STOP the daemon **zeroizes** the in-memory key. + +- **Verified reads via a Helios light client.** By default Deckard does **not trust a + third-party RPC** for on-chain reads. Reads are validated against an embedded + **Helios light client**, and the app surfaces a `ReadStatus` so you can see whether + what you're looking at is light-client-**verified** or not. A lying or compromised RPC + cannot silently feed you a false balance or state. + +- **Keystore encrypted at rest.** The wallet secret is sealed with an authenticated + envelope: a random data-encryption key (DEK) is wrapped by **Argon2id**(passphrase, salt) + and the secret is encrypted with **XChaCha20-Poly1305**. The vault stores the **BIP-39 + entropy** (not the mnemonic string, not the 64-byte seed, not a derived key); the seed is + re-derived on unlock. The vault header is fully **authenticated (AAD)** so a tamperer who + can write the file cannot downgrade KDF parameters or flip flags without failing + decryption. The file is written atomically at `0600`. A wrong passphrase fails closed + with a single generic error (no oracle). + +- **Secrets confined to `Zeroizing`, never logged.** Seeds, keys, the DEK/KEK, derived + child keys, and the transient mnemonic are held in `Zeroizing` buffers and wiped when + dropped. It is a hard project rule to **never log or `Debug`-print** a seed, key, or + passphrase, anywhere, in any crate. On an explicit Lock/STOP the daemon zeroizes the + in-memory key — a real zeroize, not just a UI route change. + +- **A hardened trust core with a strict lint policy.** `deckard-core` (the engine that does + provider/verified-reads, balances, HD-key derivation, the keystore, and the key-less + shield builder) is **`#![forbid(unsafe_code)]`**. In `deckard-core`, **`.unwrap()`, + `.expect()`, `panic!`, and raw slice indexing are denied** in non-test code — errors are + propagated with `Result`/`?`, and untrusted bytes are parsed through a bounded reader with + strict length/parameter caps (so a hostile keystore file can't OOM, hang, or corrupt state + before the AEAD can reject it). Workspace-wide, **`todo!`, `dbg!`, ignored `Result`s + (`unused_must_use`), `std::mem::forget`, and `rand::thread_rng` are denied** — the app + crate is `unsafe_code = "deny"`, and randomness comes from `OsRng`. CI fails the build on + any violation. + +### Threat model, stated honestly + +The keystore defends well against **cold theft of the vault file** (a stolen disk image, +Time Machine snapshot, backup, or world-readable copy → offline cracking). It does **not** +defend against **live malware running as your user during an unlocked session** — no +software hot wallet can, because the seed must enter RAM to sign. We minimize the +unlocked-RAM window (explicit Lock/STOP zeroizes the key), but it is inherent — and there +is **no automatic idle-lock yet** (see caveats below), so an unlocked session stays +unlocked until you lock it. A **weak passphrase** collapses the at-rest guarantee — beyond an +8-character minimum there is no passphrase-strength enforcement, so choose a strong one. + +--- + +## Known limitations & honest caveats + +We would rather under-promise. The following are real, current gaps — do not read past +them as solved: + +- **No third-party security audit yet.** An external audit is a **planned, funded + line item**, but it has not happened. Until it does, treat all guarantees above as + *self-assessed*, not independently verified. + +- **Single-maintainer review.** Deckard is currently designed, built, and reviewed by + **one person** (with cross-model adversarial review as a supplement, not a substitute). + **We are actively seeking a security co-maintainer.** If that's you, please reach out. + +- **Some critical paths are only test-covered behind `#[ignore]`.** Several end-to-end + tests (notably the shield flow and live-network paths) require `anvil` and an archive RPC + and are marked `#[ignore]`, so they **do not run under a default `cargo test`**. Some + read-path tests run against a *mocked* transport rather than live Helios, and some app + tests use a *fake* recording daemon rather than the real signer + chain. See + [`STATUS.md`](./STATUS.md) for the per-test caveats. The daemon STOP/zeroize and + propose→`Decision`→execute tests **do** run by default. + +- **Feature surface is partial — do not assume more than is built.** The encrypted + keystore + onboarding, live on-chain balances, receive (address + QR), the command + palette, Helios-verified reads, and the process-isolated signer daemon are working. The + **Shield** hero (auto-private via Railgun) is **wired and black-box tested on an Anvil + fork** — but **Send is gated** ("next release"), **Swap is a TODO**, the **agent / MCP + surface (`deckard-mcp`) is NOT built**, and the **receive-watcher auto-detect is a TODO**. + Do not rely on Send, Swap, MCP/agent automation, or the receive-watcher; they are not + finished. + +- **Supply-chain items are tracked, not closed.** Deckard vendors a native-only fork of + `eip-1193-provider` (`vendor/eip-1193-provider`, to dodge a `wasm-bindgen` exact-pin + conflict), and the upstream **Railgun** dependency's licensing is being resolved. Both are + tracked items to settle before any non-alpha release. + +- **No automatic idle-lock yet.** An automatic idle/auto-lock is *specified* (default + 15 min) in [`specs/keystore-design.md`](./specs/keystore-design.md) but is **not yet + implemented** — no timer exists in any crate. Today the daemon zeroizes the in-memory key + **only on an explicit Lock/STOP command**, never on a timer. Until idle-lock ships, an + unlocked session remains unlocked (key in RAM) until you lock it yourself. + +- **No Touch ID / biometric unlock yet.** Biometric unlock is a Phase-2 item blocked on the + macOS codesign/notarize pipeline. v0 ships **passphrase-only** unlock (Argon2id). The + passphrase is the durable ground-truth secret. + +--- + +## License & provenance + +Deckard is licensed **AGPL-3.0-or-later** (see [`LICENSE`](./LICENSE) and +[`NOTICE`](./NOTICE)). It was forked from the [`deck`](https://github.com/hellno/deck) GPUI +starter (0BSD, which permits relicensing). Source repository: +. + +--- + +*Last reviewed for `0.0.1-alpha`. This policy reflects an alpha, unaudited build and will be +tightened as the project matures toward 1.0. Nothing here should be read as a guarantee of +security for real funds.* diff --git a/crates/deckard-app/Cargo.toml b/crates/deckard-app/Cargo.toml index f79ae56..1659239 100644 --- a/crates/deckard-app/Cargo.toml +++ b/crates/deckard-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deckard-app" -version = "0.1.0" +version = "0.0.1-alpha" edition = "2021" description = "Deckard — a native, self-custodial Ethereum wallet for onchain operators (GPUI + Rust). The GUI app crate; the binary is `deckard`." license = "AGPL-3.0-or-later" diff --git a/crates/deckard-contract/Cargo.toml b/crates/deckard-contract/Cargo.toml index a892acb..fb127f5 100644 --- a/crates/deckard-contract/Cargo.toml +++ b/crates/deckard-contract/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deckard-contract" -version = "0.1.0" +version = "0.0.1-alpha" edition = "2021" license = "AGPL-3.0-or-later" description = "Deckard's frozen wire contract: Intent / Decision / Policy, the signer-daemon RPC enums, a sync Signer trait, and an in-memory MockSigner. Zero key material. Owned by docs/build/30-mcp-shape.md." diff --git a/crates/deckard-core/Cargo.toml b/crates/deckard-core/Cargo.toml index be21910..e91270c 100644 --- a/crates/deckard-core/Cargo.toml +++ b/crates/deckard-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deckard-core" -version = "0.1.0" +version = "0.0.1-alpha" edition = "2021" license = "AGPL-3.0-or-later" description = "Deckard's headless engine: Ethereum provider, balances, HD keys, and the encrypted keystore — no GPUI dependency, fully unit-testable." diff --git a/crates/deckard-signerd/Cargo.toml b/crates/deckard-signerd/Cargo.toml index 168e686..351a4db 100644 --- a/crates/deckard-signerd/Cargo.toml +++ b/crates/deckard-signerd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deckard-signerd" -version = "0.1.0" +version = "0.0.1-alpha" edition = "2021" license = "AGPL-3.0-or-later" description = "Deckard's process-isolated signer daemon: owns the decrypted key, runs the policy gate, signs + broadcasts, and answers STOP — over a same-uid Unix-domain socket. The app and the future MCP sidecar are key-less clients." diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..9a3a511 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,284 @@ +# Releasing Deckard — maintainer runbook + +A repeatable checklist for cutting a Deckard release. Written for `0.0.1-alpha`; the same steps +apply to every future cut (substitute the version string throughout). + +> **Status of what you are shipping.** Deckard is a native, self-custodial Ethereum wallet +> (GPUI + Rust; macOS + Linux). **`0.0.1-alpha` is ALPHA, pre-1.0, EXPERIMENTAL software.** +> It is **not production-ready**, has had **no third-party security audit**, and must **never** be +> used with real funds or real mainnet keys — **testnet / throwaway keys only**. Carry that framing +> into the release title, the notes, and the GIF caption. Do not imply production-readiness anywhere. +> +> What is actually built vs. gated is the ground truth in [`STATUS.md`](../STATUS.md). At +> `0.0.1-alpha`: encrypted BIP-39 keystore + onboarding, live on-chain balances (Multicall3), +> receive (address + QR), the command palette, the amber-on-near-black design system, Helios-verified +> reads, the process-isolated signer daemon, and the shield hero (wired + black-box tested on an anvil +> fork) all work. **Send is gated ("next release"), Swap is TODO, the agent/MCP surface +> (`deckard-mcp`) is not built, and the receive-watcher auto-detect is TODO.** Do not claim any of +> those four are finished in release notes. + +This runbook touches **only** docs, tags, and GitHub — it does not change engine behaviour. Releasing +is a deliberate act: take it slowly and do not skip the pre-flight. + +--- + +## Conventions (read once) + +- **Version string.** The Cargo manifest `version` **is** the full pre-release string — + e.g. `0.0.1-alpha` (SemVer pre-release syntax), not a separate `0.0.1` + a `-alpha` suffix bolted + on later. The **git tag mirrors it verbatim with a leading `v`**: manifest `0.0.1-alpha` + → tag `v0.0.1-alpha`. Keep these two in lockstep for every release. +- **The four crates** (virtual Cargo workspace, all under `crates/`): `deckard-app` (the GPUI app, + binary `deckard`), `deckard-core` (headless engine), `deckard-contract` (frozen wire contract), + `deckard-signerd` (signer daemon). All carry `license = "AGPL-3.0-or-later"`. +- **License.** AGPL-3.0-or-later — see [`LICENSE`](../LICENSE) and [`NOTICE`](../NOTICE). Deckard was + forked from the [`deck`](https://github.com/hellno/deck) GPUI starter (originally 0BSD, which + permits relicensing). Do not strip `NOTICE`; it carries the upstream + bundled-asset attributions. +- **Repo:** · **Security contact:** holgerniessner@me.com +- **Toolchain** is pinned in [`rust-toolchain.toml`](../rust-toolchain.toml) (currently `1.95.0`) — use + it; do not build a release on an ad-hoc toolchain. + +--- + +## 1. Pre-flight — Definition of Done all green, tree clean + +Do **not** start a release while any check is red. Run all four and paste the output into the release +PR / cut notes as evidence. These are verbatim the project's Definition of Done (all must hold): + +1. `cargo fmt --all --check` clean +2. `just check` green (clippy `-D warnings` on **both** the default and `--features tray` configs) +3. `cargo test --workspace` green +4. No new/changed dependencies in `Cargo.toml` / `Cargo.lock` unless explicitly approved + +```bash +cargo fmt --all --check +just check +cargo test --workspace +git diff --stat -- Cargo.toml Cargo.lock # expect: no unexpected dep churn +``` + +> **Test caveats to remember (do not treat as failures).** Some integration tests are `#[ignore]` +> and need `anvil` + an archive RPC (the shield e2e), and `anvil_e2e` silently skips if `anvil` +> isn't installed. These are documented in [`STATUS.md`](../STATUS.md) — a green `cargo test +> --workspace` is the bar for the cut; the deeper anvil/archive runs are a separate, manual +> confidence pass. + +Then confirm the working tree is clean and you are on the intended commit: + +```bash +git status --porcelain # expect: no output (clean tree) +git log --oneline -1 # the commit you are about to tag +``` + +Decide the release branch now: cut from `main` (or the merge commit that lands the release PR). Do not +tag a feature branch. + +--- + +## 2. Version bump — all 4 crate manifests + sync the lockfile + +Set the **same** `version` in every crate manifest. Note that at the first cut these may be +out of sync (some crates carried `0.1.0`); bringing them all to the release string is part of the bump. + +Edit the `version = "…"` line under `[package]` in each of: + +- `crates/deckard-app/Cargo.toml` +- `crates/deckard-core/Cargo.toml` +- `crates/deckard-contract/Cargo.toml` +- `crates/deckard-signerd/Cargo.toml` + +```toml +[package] +version = "0.0.1-alpha" +``` + +Then sync `Cargo.lock` so the recorded crate versions match (this is the *only* dependency-graph +change allowed during a routine cut — it is a version sync, not new deps): + +```bash +cargo update --workspace --offline # re-resolves the four workspace crates to the new version +# (a plain `cargo build`/`cargo check` also rewrites Cargo.lock; either is fine) +git diff -- Cargo.lock # sanity-check: only the deckard-* versions changed +``` + +> Do **not** hand-edit the GPUI git pins in `Cargo.lock`; those are bumped only via `just bump-gpui` +> (see [`docs/UPGRADING.md`](UPGRADING.md)) and are out of scope for a version cut. + +Verify all four moved together: + +```bash +git grep -nE '^version = ' -- 'crates/*/Cargo.toml' +``` + +--- + +## 3. Update `CHANGELOG.md` + +Move the accumulated `Unreleased` notes into a dated section for the new version. + +- If `CHANGELOG.md` does not exist yet (first release), create it at the repo root in + [Keep a Changelog](https://keepachangelog.com) style. +- For each subsequent release, you only do the *move*: rename `Unreleased` to the version + today's + date, then open a fresh empty `Unreleased` block at the top for the next cycle. + +```markdown +# Changelog + +All notable changes to Deckard are documented here. Format: Keep a Changelog; versioning: SemVer +(pre-release tags like `-alpha` per the convention in docs/RELEASING.md). + +## [Unreleased] + +## [0.0.1-alpha] — 2026-06-10 +### Added +- First public alpha. Encrypted BIP-39 keystore + onboarding; live on-chain balances (Multicall3); + receive (address + QR); command palette; Helios-verified reads (no third-party RPC trusted by + default); process-isolated signer daemon (`deckard-signerd`); the shield hero (auto-private via + Railgun) — wired and black-box tested on an anvil fork. + +### Known limitations (alpha) +- **Not production-ready. No third-party audit. Testnet / throwaway keys only — never real funds.** +- Send UI is gated ("next release"); Swap is TODO; the agent/MCP surface (`deckard-mcp`) is not built; + receive-watcher auto-detect is TODO. +``` + +Keep the changelog honest — it is the user-facing record. Mirror the "alpha / not for real funds" +framing here too; do not list Send / Swap / MCP / receive-watcher under "Added". + +--- + +## 4. Record the demo GIF → `docs/demo.gif` + +The GIF should show the **final** UI of the release you are cutting (record after the version bump, +against a clean build). Drive a real flow — e.g. onboarding → funded portfolio → the shield hero +making a balance private. Use testnet / throwaway keys on camera, **never** a real seed. + +**macOS prerequisites (one-time):** grant **Screen Recording** permission (System Settings → +Privacy & Security → Screen Recording) to whatever captures the screen, and **Accessibility** +permission if you script the click-through. Without both, the capture is black or the synthetic input +is dropped. + +1. **Build + run the final app:** + + ```bash + just run # builds deckard-signerd, then runs the app (the 99%-of-the-time command) + ``` + + (For a perf-true capture you can use `just run-release` instead.) + +2. **Screen-record the flow.** Use macOS screen recording (`⇧⌘5`, or your preferred recorder) to + capture the app window through the demo flow. Record at a steady pace; keep it short (a tight + ~10–20s loop reads better and stays small). + +3. **Convert the recording to a clean, small GIF.** Two-pass `palettegen`/`paletteuse` gives a sharp + palette at a small size. Target **< 8 MB**, **~12 fps**, **width ~1000px**: + + ```bash + # 1) Generate an optimized palette from the source recording. + ffmpeg -i recording.mov \ + -vf "fps=12,scale=1000:-1:flags=lanczos,palettegen=stats_mode=diff" \ + -y docs/.demo-palette.png + + # 2) Apply the palette to produce the GIF. + ffmpeg -i recording.mov -i docs/.demo-palette.png \ + -lavfi "fps=12,scale=1000:-1:flags=lanczos[v];[v][1:v]paletteuse=dither=bayer:bayer_scale=5" \ + -y docs/demo.gif + + # 3) Clean up the scratch palette and check the size. + rm docs/.demo-palette.png + ls -lh docs/demo.gif + ``` + + If it lands over ~8 MB, trim the recording, drop to `fps=10`, or narrow to `scale=900:-1`. Confirm + the GIF renders correctly (open it / preview the README) before committing. + +4. Reference `docs/demo.gif` from the README if it isn't already, and commit it with the release. + +--- + +## 5. Tag + GitHub release + +Commit the version bump + changelog (+ GIF) first, on the release commit. Then create an **annotated** +tag whose message becomes the release body, push it, and publish a **pre-release** on GitHub. + +```bash +# Commit the cut (version bump, CHANGELOG, demo.gif). +git add crates/*/Cargo.toml Cargo.lock CHANGELOG.md docs/demo.gif +git commit -m "release: 0.0.1-alpha" + +# Annotated tag — mirrors the manifest version with a leading v. +# The annotation text is reused as the release notes via --notes-from-tag below. +git tag -a v0.0.1-alpha -m "Deckard 0.0.1-alpha — first public alpha + +ALPHA / EXPERIMENTAL / pre-1.0. Not production-ready, no third-party security audit. +Testnet or throwaway keys only — do NOT use with real funds or real mainnet keys. + +Works: encrypted BIP-39 keystore + onboarding, live on-chain balances, receive (address + QR), +command palette, Helios-verified reads, process-isolated signer daemon, the shield hero (wired + +black-box tested on an anvil fork). +Not yet: Send (gated, next release), Swap (TODO), agent/MCP surface (not built), receive-watcher (TODO). + +License: AGPL-3.0-or-later. Security contact: holgerniessner@me.com" + +# Push the commit and the tag. +git push origin main # or the release branch you cut from +git push origin v0.0.1-alpha + +# Publish a PRE-RELEASE on GitHub, reusing the tag annotation as the notes. +gh release create v0.0.1-alpha \ + --prerelease \ + --title "Deckard 0.0.1-alpha (experimental)" \ + --notes-from-tag +``` + +`--prerelease` is **required** for an alpha — it keeps GitHub from marking it "Latest" and signals to +visitors this is not a stable build. + +### Optionally attach the macOS app bundle + +If you want a downloadable build on the release, produce the `.app` and attach it. Make clear in the +asset description / notes that it is unsigned, experimental, and testnet-only. + +```bash +just bundle # → target/release/bundle/osx/Deckard.app (needs: cargo install cargo-bundle) + +# Zip the .app (GitHub release assets must be single files), then upload it to the release. +ditto -c -k --sequesterRsrc --keepParent \ + target/release/bundle/osx/Deckard.app Deckard-0.0.1-alpha-macos.app.zip +gh release upload v0.0.1-alpha Deckard-0.0.1-alpha-macos.app.zip +``` + +> The bundle is **not code-signed or notarized**; on first launch macOS Gatekeeper will warn. That is +> expected for an experimental alpha. Mention it in the release notes so users aren't surprised. + +--- + +## 6. Visibility — flipping private → public is a deliberate decision + +Making the repository public is a **conscious maintainer choice, not a routine step of cutting a +release.** A version can be tagged and a (pre-)release published while the repo stays private. Before +flipping `hellno/deckard` from private to public, deliberately confirm: + +- The security framing is unambiguous everywhere a newcomer lands (README, release notes, this + runbook): **alpha, unaudited, testnet/throwaway keys only, never real funds.** +- `LICENSE` (AGPL-3.0-or-later) and `NOTICE` (upstream `deck` + bundled-asset attributions) are + present and accurate. +- No secrets, seeds, or private keys are anywhere in the history. Nothing in `Zeroizing`-protected + paths leaked into a fixture, log, or commit. +- The security contact (holgerniessner@me.com) is discoverable for responsible disclosure. + +Only when all of that holds should a maintainer flip visibility. Treat it as its own reviewed action. + +--- + +## Quick reference + +| Step | Command(s) | +|---|---| +| Pre-flight DoD | `cargo fmt --all --check` · `just check` · `cargo test --workspace` · clean `git status` | +| Bump version | edit `version` in all 4 `crates/*/Cargo.toml`, then sync `Cargo.lock` | +| Changelog | move `Unreleased` → `[0.0.1-alpha] — ` in `CHANGELOG.md` | +| Demo GIF | `just run` → record → `ffmpeg` palettegen/paletteuse → `docs/demo.gif` | +| Tag + release | `git tag -a v0.0.1-alpha …` · `git push origin v0.0.1-alpha` · `gh release create … --prerelease --notes-from-tag` | +| Bundle (optional) | `just bundle` → zip → `gh release upload` | +| Go public | deliberate maintainer decision, **not** part of the routine cut | diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 0000000..229e9b2 Binary files /dev/null and b/docs/demo.gif differ