Guide

Back up and recover a book

Every save backs up the previous file; a .log journal records changes between saves, and book.replayJournal puts them back after a crash.

red-ale protects your book the way GnuCash does, with two safety nets. Each save first copies the old file to a timestamped backup. Between saves, every change is also written to a .log journal beside the book. If a session ends before you save, the journal still has the changes, and you can replay them onto the last saved file.

Steps

  1. Save as usual with book.save. The result's backup is the path of the copy of the previous file (book.gnucash becomes book.gnucash.<timestamp>.gnucash), and removed lists old backups and journals that passed their retention. red-ale exec --retention-days 30 sets how long they are kept (30 by default); --keep-backups-forever keeps them all.
  2. Find the journal. book.info gives its path as journal: a file named <book>.<timestamp>.log, written while the book is open.
  3. After a crash, open the last saved book (not a backup, unless you mean to go back in time) and call book.replayJournal with the journal's path. It creates, updates or deletes each transaction by GUID. created, updated and deleted list what it touched, and notFound lists records whose transaction was missing.
  4. Check and save. Look at the result and at book.info, then call book.save to keep the recovered work.
  5. To go back to an older state, close the book and copy a backup over it, or open the backup directly. A backup is an ordinary book file.

Replay changes transactions only, not accounts. If a journal record needs an account the book no longer has, the replay refuses that record unless you pass skipInvalid: true, which skips and lists it.

Example

Saving after a change returns the path of the backup; replaying a journal that does not exist is refused cleanly.

Example: with a copy of the reports/test-data example book open as book.gnucash.

JSON-RPC

Request:

{"jsonrpc":"2.0","id":1,"method":"account.create","params":{"name":"Savings","type":"BANK","parent":"Root:Asset","commodity":"USD"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "…"
}

Request:

{"jsonrpc":"2.0","id":2,"method":"book.save","params":{}}

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "backup": "…",
    "removed": [],
    "compressed": false
  }
}

Request:

{"jsonrpc":"2.0","id":3,"method":"book.replayJournal","params":{"path":"lost-session.log"}}

Response:

{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": 1008,
    "message": "…",
    "data": {
      "path": "…",
      "help": "error/io-error"
    }
  }
}

Command line

red-ale exec --book book.gnucash --script - <<'EOF'
account.create {"name":"Savings","type":"BANK","parent":"Root:Asset","commodity":"USD"}
book.save {}
book.replayJournal {"path":"lost-session.log"}
EOF

It fails with exit status 2, printing on standard error:

{
  "error": {
    "code": 1008,
    "message": "…",
    "data": {
      "path": "…",
      "help": "error/io-error"
    }
  }
}

MCP tool call

Tool call:

{"name":"account.create","arguments":{"name":"Savings","type":"BANK","parent":"Root:Asset","commodity":"USD"}}

Result:

{
  "content": [
    {
      "type": "text",
      "text": "\"…\""
    }
  ]
}

Tool call:

{"name":"book.save","arguments":{}}

Result:

{
  "structuredContent": {
    "backup": "…",
    "removed": [],
    "compressed": false
  }
}

Tool call:

{"name":"book.replayJournal","arguments":{"path":"lost-session.log"}}

Result:

{
  "isError": true,
  "structuredContent": {
    "error": {
      "code": 1008,
      "name": "io-error",
      "message": "…",
      "data": {
        "path": "…",
        "help": "error/io-error"
      }
    }
  }
}