diff options
| -rw-r--r-- | README.md | 155 |
1 files changed, 151 insertions, 4 deletions
@@ -3,13 +3,61 @@ Self-hosted bookkeeping for Swedish organizations. One C daemon, one SQLite file, one JSON API shared by the CLI, the ncurses UI and LLM agents. -Status: **0.1.0-dev — walking skeleton.** Not production ready. See +Status: **0.1.0-dev — working, not production-proven.** See `docs/COMPLIANCE.md` §9. +## Features + +Everything below lives in the daemon and one SQLite file; every feature is a +JSON command in `docs/PROTOCOL.md`, so the TUI, the CLI and an AI agent can +do the same things. + +**Bookkeeping** +- BAS chart of accounts (K2 and K3 variants), per organization; multi-org with + owner/bookkeeper/viewer roles. +- Verifikat with unbroken numbering per series and fiscal year, balanced + rows in integer öre, ändringsverifikat for corrections — nothing is ever + edited or deleted. +- Konteringsmallar: reusable templates with a small formula language + (`x*0.25`, `-x`), applied at posting time. +- Underlag: attachments stored in the database (SHA-256 verified, + append-only), an inbox for unlinked documents. +- Fiscal years, period locks, ingående balans, year-end close. + +**Reports and filing** +- Saldobalans, resultatrapport, balansrapport, huvudbok, + verifikationslista — Kapitas-style tables in the TUI, JSON on the API. +- Momsrapport ruta för ruta with per-org editable rules; eSKD XML export + for the momsdeklaration. +- Bokslut: automated year-end postings (avskrivningar, periodiseringsfond, + skatt, resultatdisposition) from a dry-run plan. +- Årsredovisning K2 text draft and INK2/SRU files (INK2, INK2R, INK2S). +- SIE 4 import and export (CP437), multi-year history import. + +**Invoicing, bank and payroll** +- Customer register; invoices rendered as PDF (own renderer, no + dependencies), OCR numbers, issue = number + PDF + voucher in one + transaction, e-mail over SMTP with the password encrypted at rest. +- Bank reconciliation: import SEB CSV statements, match against vouchers, + create prefilled vouchers from unmatched transactions. +- Payroll engine: employees, Skatteverket tax tables, monthly runs with + preview/post, payslips (PDF + e-mail) and AGI underlag — API and TUI + complete (Lönekörningar, Anställda, Skattetabeller). + +**Compliance and operations** (`docs/COMPLIANCE.md`) +- SHA-256 hash chains over vouchers and the audit log; `audit.verify` + recomputes them and re-hashes attachments. +- Every mutation is audited; secrets never reach the log. +- `backup.snapshot` (`VACUUM INTO`) for consistent backups; pre-migration + snapshots before schema upgrades. +- Unix socket, optional TLS listener with certificate reload, Argon2id + passwords, scoped revocable API tokens, login rate limiting. + ## Build -Requirements: Linux, a C11 compiler, GNU make, and libncursesw for the TUI. -No other system libraries — SQLite, yyjson, SHA-256 and Argon2 are vendored. +Requirements: Linux, a C11 compiler, GNU make, libncursesw for the TUI and +OpenSSL (`libssl-dev`) for TLS and SMTP. SQLite, yyjson, SHA-256 and Argon2 +are vendored. ```sh make -j$(nproc) @@ -28,7 +76,9 @@ Produces `build/bokfd` (daemon), `build/bokfctl` (CLI) and `build/bokftui` ```sh ./build/bokfd init --db /var/lib/bokfd/bokfd.db --user admin -BOKFD_PASSWORD=secret ./build/bokfd init ... # non-interactive +# non-interactive, same command with the password in the environment: +BOKFD_PASSWORD=secret ./build/bokfd init --db /var/lib/bokfd/bokfd.db \ + --user admin ./build/bokfd --db /var/lib/bokfd/bokfd.db --socket /run/bokfd/bokfd.sock ``` @@ -43,11 +93,108 @@ BOKFD_USER=admin BOKFD_PASSWORD=secret \ ./build/bokfctl --socket /run/bokfd/bokfd.sock org.create '{"name":"AB Ett","org_nr":"5560123456"}' ``` +## Run the backend with Docker + +The image carries `bokfd` and `bokfctl` only (Alpine, ~30 MB). It is built +from the checkout; there is no registry. The TUI is built on the machine you +sit at and talks to the container over TLS. + +```sh +# 1. Image +docker build -t bokf:dev . + +# 2. A directory for compose.yaml, .env and the state (var/db, var/run) +mkdir -p ~/bokf && cp compose.yaml ~/bokf/ && cd ~/bokf +printf 'BOKF_IMAGE=bokf\nBOKF_TAG=dev\nLEGO_DOMAIN=localhost\n' > .env + +# 3. A certificate. Self-signed is fine on a LAN; compose.yaml also has a +# lego sidecar for Let's Encrypt via DNS-01 (see docs/DEPLOY.md). +mkdir -p var/db/certs/certificates +openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \ + -days 3650 -subj /CN=localhost -addext subjectAltName=DNS:localhost \ + -keyout var/db/certs/certificates/localhost.key \ + -out var/db/certs/certificates/localhost.crt + +# 4. Create the database and the admin user, then start +docker compose run --rm -e BOKFD_PASSWORD=secret123 bokfd init --user admin +docker compose up -d bokfd +docker compose ps # wait for "healthy" +``` + +Then from any machine that trusts the certificate: + +```sh +export BOKFD_SOCKET=tls:localhost:8788 \ + BOKFD_TLS_CA=~/bokf/var/db/certs/certificates/localhost.crt +BOKFD_USER=admin BOKFD_PASSWORD=secret123 ./build/bokfctl health +BOKFD_USER=admin BOKFD_PASSWORD=secret123 ./build/bokftui +``` + +`init` refuses to run twice. State lives in `var/db` (database, `backup/`, +`export/`, `certs/`) and `var/run` (the Unix socket, for clients on the same +host). Set `BOKFD_SECRET_KEY` in `.env` (32 random bytes, hex or base64) +before storing SMTP passwords or employee personnummer (payroll needs it). +Use `LEGO_DOMAIN`, `LEGO_EMAIL`, +`INWX_USERNAME`/`INWX_PASSWORD` for a real certificate, and +`scripts/deploy.sh` to ship releases to a remote host over SSH — both in +`docs/DEPLOY.md`. + +## Working with an AI agent + +bokf is designed so that an LLM agent can do the bookkeeping, with a human +approving what matters: + +- **One protocol, self-describing.** `describe` returns every command with + its permission, argument schema and defaults; `agent.instructions` returns + the workflow rules in Markdown. An agent needs no other documentation. +- **Safe by construction.** Every mutation supports `dry_run:true`, and + postings carry an idempotent `client_ref`; the ledger is append-only, so a + wrong posting is corrected by a new voucher, never hidden. Locked periods + and closed years are hard stops the agent cannot override. +- **Least privilege.** Give the agent a scoped API token (`read`, `write`, + never `admin`), bound to one organization, shown once and revocable. Owner + actions (closing a year in the TUI, token administration in `bokfctl`) + stay with the human. +- **Fully audited.** Every call the agent makes is in the hash-chained audit + log with the token that made it. + +Setup is one command per agent: + +```sh +BOKFD_USER=admin BOKFD_PASSWORD=… ./build/bokfctl --org 1 token.create \ + '{"label":"claude","scopes":["read","write"]}' +``` + +Point the agent at the socket (or `tls:host:8788`) and the token, tell it to +fetch `agent.instructions` first, and prompts like these are enough: + +- *"Post the September invoices from `~/underlag/2026-09/` — attach each PDF, + use the `Försäljning 25 %` template, dry-run everything and show me the + list before posting."* +- *"Import `seb-2026-09.csv`, match what you can against existing vouchers, + and prepare — but don't post — vouchers for the rest."* +- *"Give me the momsrapport for Q3, explain any box that changed more than + 20 % from Q2, and save the eSKD file."* +- *"Run the October payroll preview for all employees and list the + differences from September."* + +`bokfctl` speaks the same NDJSON protocol from shell scripts and MCP-style +tool wrappers alike: `bokfctl <command> '<json args>'`. + +The same applies to developing bokf. `AGENTS.md` holds the invariants and +the recipe for adding a command; `make check` fails when code and +`docs/PROTOCOL.md` drift; `make gate` is the pre-push bar. Most of the +project was written by agents working from short briefs. + ## Documentation - `docs/PROTOCOL.md` — transport, auth, command reference - `docs/SCHEMA.md` — database schema, invariants, hash chains - `docs/COMPLIANCE.md` — BFL/BFNAR mapping and operator duties +- `docs/DEPLOY.md` — Docker, TLS certificates, deploying to a remote host +- `docs/INVOICING.md`, `docs/PAYROLL.md` — design notes for those modules +- `docs/TUI-GUIDELINES.md` — keys, widgets and conventions for the TUI +- `AGENTS.md` — how to work on the code (for humans and agents) ## License |
