feat: end-to-end integrity checks with self-healing; website; installer and Windows fixes
Store::verify compares the whole SQLite projection, every table row for row, against a fresh projection of the journal; verify_and_heal rebuilds a drifted database (keeping the old file) at every server start and via the new books.verify tool, which also re-hashes every blob. The GUI checks the server's database read-only and every stored file, and shows the result. rebuild() also drops stale WAL/SHM files. site/: the wanderbooks website (just site, just publish). install.sh: OS detection (Windows points to WSL2), C compiler and git checks, WANDERBOOKS_REPO. Windows: no directory fsync off Unix; .exe-aware lookup. Repo justfile: check, build, install, demo, site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
This commit is contained in:
25 files changed
+808
-41
No files matched your search
+172
@@ -0,0 +1,172 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>wanderbooks — bookkeeping you talk to</title>
|
||||
<meta name="description" content="Double-entry bookkeeping run by an AI bookkeeper. You talk; it keeps the books. You watch every number, live. Open source, your files, on your machine.">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f6f6f4; --surface: #fff; --border: #e4e4e0; --text: #1b1b1f;
|
||||
--muted: #67676f; --faint: #9c9ca4; --accent: #4f46e5; --accent-soft: #eeeefd;
|
||||
--code-bg: #1b1b1f; --code-text: #ececee; --positive: #15803d;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #111214; --surface: #191a1d; --border: #2a2b30; --text: #ececee;
|
||||
--muted: #a3a3ac; --faint: #6e6e78; --accent: #8b93fa; --accent-soft: #242545;
|
||||
--code-bg: #0b0b0d; --code-text: #ececee; --positive: #4ade80;
|
||||
}
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html { -webkit-text-size-adjust: 100%; }
|
||||
body {
|
||||
margin: 0; background: var(--bg); color: var(--text);
|
||||
font: 400 17px/1.6 Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
font-feature-settings: "tnum", "cv11";
|
||||
}
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.wrap { max-width: 1120px; margin: 0 auto; padding: 0 24px; }
|
||||
header { display: flex; align-items: center; justify-content: space-between; padding: 22px 0; }
|
||||
.brand { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 17px; color: var(--text); }
|
||||
.brand .mark { width: 28px; height: 28px; border-radius: 8px; background: var(--accent); color: #fff; display: grid; place-items: center; font-weight: 700; }
|
||||
nav a { color: var(--muted); margin-left: 22px; font-size: 15px; }
|
||||
.hero { padding: 56px 0 36px; max-width: 820px; }
|
||||
h1 { font-size: clamp(38px, 6vw, 60px); line-height: 1.05; letter-spacing: -0.03em; margin: 0 0 20px; font-weight: 700; }
|
||||
.lede { font-size: clamp(18px, 2.2vw, 21px); color: var(--muted); margin: 0 0 30px; max-width: 700px; }
|
||||
.install { background: var(--code-bg); color: var(--code-text); border-radius: 12px; padding: 16px 18px; display: flex; gap: 14px; align-items: flex-start; font: 500 14.5px/1.55 ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
|
||||
.install code { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
|
||||
.install .prompt { color: #8b93fa; user-select: none; }
|
||||
.copy { flex: none; background: #2a2b30; color: #ececee; border: 0; border-radius: 8px; padding: 7px 12px; font: 500 13px Inter, sans-serif; cursor: pointer; }
|
||||
.copy:hover { background: #3a3b42; }
|
||||
.note { color: var(--faint); font-size: 14px; margin-top: 12px; }
|
||||
.shot { margin: 40px 0 8px; border-radius: 14px; border: 1px solid var(--border); overflow: hidden; box-shadow: 0 24px 60px -24px rgba(0,0,0,.25); background: var(--surface); }
|
||||
.shot img { display: block; width: 100%; height: auto; }
|
||||
.shot.dark { display: none; }
|
||||
@media (prefers-color-scheme: dark) { .shot.light { display: none; } .shot.dark { display: block; } }
|
||||
.caption { color: var(--faint); font-size: 14px; text-align: center; margin: 10px 0 0; }
|
||||
section { padding: 64px 0 8px; }
|
||||
h2 { font-size: clamp(26px, 3.4vw, 34px); letter-spacing: -0.02em; line-height: 1.15; margin: 0 0 14px; }
|
||||
.sub { color: var(--muted); max-width: 720px; margin: 0 0 32px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
|
||||
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 22px; }
|
||||
.card h3 { margin: 0 0 8px; font-size: 18px; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 15.5px; }
|
||||
.card .k { color: var(--accent); font-weight: 600; font-size: 13px; letter-spacing: .04em; text-transform: uppercase; margin-bottom: 8px; }
|
||||
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: center; }
|
||||
.two .shot { margin: 0; }
|
||||
ul.ticks { list-style: none; padding: 0; margin: 0; }
|
||||
ul.ticks li { padding: 7px 0 7px 28px; position: relative; color: var(--muted); }
|
||||
ul.ticks li b { color: var(--text); font-weight: 600; }
|
||||
ul.ticks li::before { content: "✓"; position: absolute; left: 0; color: var(--positive); font-weight: 700; }
|
||||
.platforms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
|
||||
.platforms .card p code { font-size: 13.5px; }
|
||||
code.inline { font: 500 14px ui-monospace, Menlo, Consolas, monospace; background: var(--accent-soft); padding: 2px 6px; border-radius: 6px; color: var(--text); }
|
||||
footer { padding: 64px 0 48px; color: var(--faint); font-size: 14px; display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
|
||||
@media (max-width: 860px) {
|
||||
.grid, .platforms, .two { grid-template-columns: 1fr; }
|
||||
nav { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<a class="brand" href="#"><span class="mark">w</span>wanderbooks</a>
|
||||
<nav>
|
||||
<a href="#how">How it works</a>
|
||||
<a href="#trust">Trust</a>
|
||||
<a href="#install">Install</a>
|
||||
<a href="https://git.swaits.com/swaits/wanderbooks">Source</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="hero">
|
||||
<h1>Bookkeeping you talk to.</h1>
|
||||
<p class="lede">wanderbooks is real double-entry accounting run by an AI bookkeeper. You tell it what happened and hand it receipts; it keeps the books, balanced to the cent. You watch every number, live, and click through any of them to the receipt behind it.</p>
|
||||
<div class="install" id="install-box">
|
||||
<span class="prompt">$</span>
|
||||
<code id="cmd">curl -fsSL https://git.swaits.com/swaits/wanderbooks/raw/branch/main/install.sh | sh -s -- ~/Books/YourCompany</code>
|
||||
<button class="copy" id="copy">Copy</button>
|
||||
</div>
|
||||
<p class="note">One line: installs everything, makes your books, and starts your bookkeeper. Linux and macOS; Windows via WSL2.</p>
|
||||
</div>
|
||||
|
||||
<div class="shot light"><img src="assets/overview.png" alt="The wanderbooks overview: cash, what you owe, spending, revenue, runway, a cash chart, and what needs attention" width="1440" height="920"></div>
|
||||
<div class="shot dark"><img src="assets/overview-dark.png" alt="The wanderbooks overview in dark mode" width="1440" height="920"></div>
|
||||
<p class="caption">The overview of a demo company. It leads with the answer, then the evidence.</p>
|
||||
|
||||
<section id="how">
|
||||
<h2>You talk. It keeps the books. You see everything.</h2>
|
||||
<p class="sub">No forms, no categories to hunt for, no month-end scramble. The bookkeeper works through an accounting engine that refuses to be wrong, and the books are always open in front of you.</p>
|
||||
<div class="grid">
|
||||
<div class="card"><div class="k">Talk</div><h3>Your bookkeeper is an AI agent</h3><p>“Record the Apple developer fee, the receipt's in my email.” It drafts the entry, shows you the preview, attaches the receipt, and posts when you say so. Claude Code, via MCP.</p></div>
|
||||
<div class="card"><div class="k">See</div><h3>A desktop view of everything</h3><p>Cash and runway, profit by month, every transaction with its rendered receipt, statements an accountant would sign off on. Changes the agent makes appear in milliseconds.</p></div>
|
||||
<div class="card"><div class="k">Own</div><h3>Your books are files you keep</h3><p>An append-only journal, your receipts, and a config file, in a folder under git. No subscription, no lock-in, no cloud you don't control.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="two">
|
||||
<div>
|
||||
<h2>Every number goes all the way down.</h2>
|
||||
<p class="sub">Click an amount on a statement and you get the transactions behind it, for exactly those dates. Click a transaction and you get its lines, the receipt itself, and every event in its history.</p>
|
||||
<ul class="ticks">
|
||||
<li><b>Statements an accountant expects:</b> profit and loss, balance sheet, cash flow, owners' equity, trial balance, general ledger.</li>
|
||||
<li><b>By month, quarter, or year</b>, compared with the prior period, each line as a share of income.</li>
|
||||
<li><b>Tax-ready:</b> tax categories by return (Schedule C, 1065, 1120-S, 1120), a 1099 worksheet, receivables and payables aging.</li>
|
||||
<li><b>Real-business features:</b> invoices and bills, fixed assets and depreciation, prepaid expenses, recurring entries, platform payouts grossed up, classes, multi-currency lines.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="shot"><img src="assets/entry.png" alt="A transaction with its lines, its rendered receipt, and its history" width="1440" height="920"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="trust">
|
||||
<h2>Built so you can trust it.</h2>
|
||||
<p class="sub">An AI is doing your books, so the books have to be impossible to quietly get wrong.</p>
|
||||
<div class="grid">
|
||||
<div class="card"><h3>The engine enforces accounting</h3><p>Every entry balances exactly; money is exact decimal, never floating point. Posted entries are permanent; corrections are reversing entries. Receipts are required over your threshold, enforced by the server.</p></div>
|
||||
<div class="card"><h3>A tamper-evident journal</h3><p>Every change is an event, hash-chained to the one before it. The database is rebuilt from the journal and checked against it, table by table; every receipt is checked against its hash. The app shows you the result.</p></div>
|
||||
<div class="card"><h3>You approve what matters</h3><p>The agent previews every posting before it happens. The desktop view can't change anything; when it spots a problem it hands you a prompt for the agent.</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="install">
|
||||
<h2>Install</h2>
|
||||
<p class="sub">The one line above does everything. It installs Rust if you don't have it, builds wanderbooks, makes your books folder with the bookkeeper wired in, and starts a session to set up your company.</p>
|
||||
<div class="platforms">
|
||||
<div class="card"><h3>Linux</h3><p>Needs a C compiler and git (<code class="inline">build-essential</code> on Debian and Ubuntu). The installer tells you if anything's missing.</p></div>
|
||||
<div class="card"><h3>macOS</h3><p>Needs Apple's command line tools: <code class="inline">xcode-select --install</code>.</p></div>
|
||||
<div class="card"><h3>Windows</h3><p>Run it inside WSL2 (<code class="inline">wsl --install</code>, then Ubuntu). On Windows 11 the desktop view opens as a normal window. Native Windows is on the way.</p></div>
|
||||
</div>
|
||||
<p class="note" style="margin-top:24px">Already installed? <code class="inline">wanderbooks new ~/Books/AnotherCompany</code>. Inside a books folder, <code class="inline">just gui</code> opens the desktop view and <code class="inline">just agent</code> starts your bookkeeper. You'll need <a href="https://claude.com/claude-code">Claude Code</a>.</p>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<span>wanderbooks is open source (MIT or Apache-2.0). It keeps books; it isn't tax or legal advice.</span>
|
||||
<span><a href="https://git.swaits.com/swaits/wanderbooks">Source</a></span>
|
||||
</footer>
|
||||
</div>
|
||||
<script>
|
||||
// When served next to install.sh, point the one-liner at this site.
|
||||
(function () {
|
||||
var cmd = document.getElementById("cmd");
|
||||
if (location.protocol === "https:") {
|
||||
cmd.textContent = "curl -fsSL " + location.origin + "/install.sh | sh -s -- ~/Books/YourCompany";
|
||||
}
|
||||
document.getElementById("copy").addEventListener("click", function () {
|
||||
var b = this;
|
||||
navigator.clipboard.writeText(cmd.textContent).then(function () {
|
||||
b.textContent = "Copied";
|
||||
setTimeout(function () { b.textContent = "Copy"; }, 1500);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user