summaryrefslogtreecommitdiff
path: root/README.md
blob: fc1aa3314816ec78e2ab18e93e614f9e8a32bc02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# bokf

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 — 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, libncursesw for the TUI and
OpenSSL (`libssl-dev`) for TLS and SMTP. SQLite, yyjson, SHA-256 and Argon2
are vendored.

```sh
make -j$(nproc)
make test
```

`make test` runs the server/protocol suite, the TUI unit tests and a docs/code
consistency check. Extra targets: `make test-asan` / `make test-ubsan`
(sanitizers) and `make test-pty` (drives the real TUI over a pty against a
throwaway `/tmp` daemon and asserts on screen text).

Produces `build/bokfd` (daemon), `build/bokfctl` (CLI) and `build/bokftui`
(ncurses UI). Builds on x86_64 and arm64 (Armbian, Raspberry Pi, …).

## Run

```sh
./build/bokfd init --db /var/lib/bokfd/bokfd.db --user admin
# 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
```

In another shell, the terminal UI (or script with `bokfctl`):

```sh
BOKFD_SOCKET=/run/bokfd/bokfd.sock BOKFD_USER=admin BOKFD_PASSWORD=secret \
  ./build/bokftui

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

GPL-3.0-or-later. Vendored components have their own compatible licenses; see
`THIRD_PARTY_NOTICES.md`.