aboutsummaryrefslogtreecommitdiff
path: root/docs/STATE.md
blob: 0f8a9151cce902b33c4815a30f028387d82d2971 (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
# bokf — project state

Snapshot for resuming work in a new session. Read with `AGENTS.md` (rules)
and `docs/TUI-GUIDELINES.md` (UI conventions). Dated 2026-09-18.

## Status

Working self-hosted bookkeeping system, not production-proven. Backend ledger
core is complete; filing/year-end/payroll are not. TUI is usable and
exercised via pty smoke tests; the test suite (`make test`) covers the
server/protocol/ledger only.

## Locked decisions

1. **Name/license**: `bokf`, daemon `bokfd`, clients `bokfctl` (scriptable)
   and `bokftui` (ncurses); GPL-3.0-or-later; repo `~/work/bokf`.
2. **Stack**: C11, Makefile, vendored SQLite 3.53.4 / yyjson 0.13.0 / Argon2
   20190702 / SHA-256 (public domain). Only system dep: libncursesw.
3. **Storage**: SQLite WAL, `synchronous=FULL`, STRICT tables, composite-key
   tenant isolation, append-only triggers, `VACUUM INTO` snapshots. Postgres
   deliberately rejected for now; keep DB access behind one layer for a later
   port.
4. **Protocol**: NDJSON over Unix socket (+ optional plain TCP and a native
   TLS listener), protocol v1.
   `dry_run` on every mutation, `client_ref` idempotency, stable error codes,
   `describe` + `agent.instructions`, money in integer öre.
5. **Auth**: multi-org; memberships owner/bookkeeper/viewer; API tokens bound
   to user+org with scopes, shown once, revocable; sessions in memory;
   Argon2id. Server messages English, UI Swedish.
6. **Compliance design**: SHA-256 audit chain, SHA-256 voucher chain
   (canonical encoding in `SCHEMA.md` §7.1/§9.1), period locks, fiscal year
   close, corrections only as ändringsverifikat, SIE 4 (CP437) in/out.
7. **Verifikat ids**: series is free text (`A`, `V-`, `A ` …); unbroken
   numbering per fiscal year+series; id displayed as `series+number`
   (`V-8`). Org setting `default_series` (Inställningar) for new vouchers and
   new templates.
8. **Templates** (`konteringsmallar`): server-side; formula language over `x`
   with `+ - * /` and parentheses, positive=debit, negative=credit, zero rows
   dropped; rounding remainder assigned to the largest row; `{x}` in the
   description; `template.*` commands + `voucher.post {template,x}`; archive
   instead of delete.
9. **Ingående balans**: one series `IB` voucher per fiscal year (dated at
   year start). Reports treat series IB as IB, not period movement; SIE
   export/import round-trips without double counting; TUI editor shows the
   year's *effective* opening balances (carry-forward plus IB vouchers) and
   posts deltas, so nothing is ever edited and a target balance can be
   entered directly.
10. **Attachments**: content stored in the DB (BLOB), immutable, linked via
    append-only `voucher_attachments`. Default limit 10 MiB
    (`max_attachment_bytes`); the socket line limit is derived from it
    (base64). TUI `Ctrl+F` attaches via file browser; the client checks the
    size from `meta` first. Setting `attachment_dir` (tilde expanded).
11. **UI keys**: Ctrl+N = add, F5 = refresh, Esc/q = back (never exits),
    Ctrl+C = quit, 1–9/g = jump, F7 = clear row in editors, F9 = save.
    Shared line editor and date field; see TUI-GUIDELINES.md.
12. **Login**: two steps — credentials, then org picker ("Välj organisation
    att representera"). No org switch in the dashboard; fiscal year switch is
    on the dashboard. `--org ID` bypasses the picker.
13. **Settings**: `settings.get`/`settings.set`; keys `default_series`,
    `attachment_dir`.
14. **Transport**: Unix socket for host clients; plain TCP loopback-only;
    native TLS listener (`BOKFD_TLS`, `BOKFD_TLS_CERT/KEY`, OpenSSL, TLS 1.2+,
    cert reload on file change) with client targets `tls:host:port` and
    system-trust verification (`BOKFD_TLS_CA` for private CAs). Certificates
    come from a lego sidecar using INWX DNS-01 (`compose.yaml`). Externals
    get accounts/roles/tokens, never VPN access. `scripts/deploy.sh` builds
    locally and ships over SSH, or builds on the host when architectures
    differ. `scripts/deploy.sh --dev` cross-compiles the binaries here and
    hot-reloads the daemon (SIGHUP re-exec via `docker cp`), skipping the
    image build and container recreate.
15. **Reports in the TUI**: rendered as fixed-width Swedish tables that mirror
    the Kapitas PDF exports (Saldobalans, Resultatrapport with previous-year
    column and 89xx bokfört/ej bokfört, Balansrapport with Ing balans/Ing
    saldo/Period/Utg balans and Beräknat resultat, Momsrapport ruta för ruta).
    The TUI never shows report JSON. Amounts are Swedish formatted
    (`1 234,56`); moms rutas are whole kronor truncated like Kapitas.
16. **Moms rules (schema v3)**: default seed covers 05 over 3000-3019,
    3100-3199, 3300-3399, reverse-charge sales (32xx) in 41, EU purchases in
    20/21, reverse-charge output VAT 2614/2624/2634 in 30/31/32, and box 48
    signed negative. Rules may share a box and are summed; box 49 is the sum
    of the moms boxes only. v3 migrates existing databases.

## Pending decisions

- Link attachments after posting (API already supports
  `attachment.put {voucher_id}`): proposed TUI actions — `Ctrl+F` in the
  voucher detail to upload+link, and selecting an inbox item and pressing a
  key to link it to a voucher picked from a list. Waiting for a go-ahead.
- Priority between **eSKD moms filing** and **bokslut/K2+SRU** for the next
  backend milestone (eSKD was suggested first).
- Moms `report_rules` seed is a corrected starter mapping (schema v3), but
  there is still no command/TUI to edit rules per org; add one before filing
  if the mapping needs adjustments (SCHEMA.md §10 promises owner editing).

## Backlog (prioritized, from COMPLIANCE.md §10 and the audit)

1. eSKD file generation for momsdeklaration.
2. Bokslut automation (avskrivningar, periodiseringsfond, skatt,
   resultatdisposition).
3. K2 årsredovisning document + SRU files (INK2/INK2R/INK2S).
4. `audit.verify` must also verify the **voucher** hash chain (today only the
   audit chain is verified).
5. `report.general_ledger` and `report.voucher_list` (documented, not
   implemented).
6. `describe` argument schemas (currently name/summary/permission only).
7. Pre-migration `VACUUM INTO` snapshot (promised in SCHEMA.md, not built).
8. ~~Docker image + compose (multi-arch amd64/arm64, GHCR) and systemd unit.~~
   Done as a Dockerfile + `compose.yaml` (amd64/arm64 build stage) and
   `scripts/deploy.sh` over SSH; no registry and no systemd unit (the
   container is the unit).
9. Password change, user disable, TOTP.
10. Bank import/reconciliation (CSV first, then PSD2), invoicing/reskontra,
    AGI/payroll if employees.
11. ~~SIE import only into an empty fiscal year; consider broader import.~~
    Chronological multi-year import works (CRLF, `#RAR 0`, zero rows, `#IB`
    rule handled); each year must still target an empty fiscal year. Note:
    years whose source system kept corrected `#IB`/`#UB` that the vouchers
    do not reproduce (like the Kapitas 2022-2026 books) diverge from the
    source when imported as history; the latest year imports exactly.
12. TUI polish: horizontal scrolling in long text fields, bracketed paste.

## Environment / how to run

- Demo: db `~/bokf-demo/bokfd.db`, socket `~/bokf-demo/bokfd.sock`,
  pid file `~/bokf-demo/bokfd.pid`; login `admin` / `demo1234`.
  Start TUI: `cd ~/work/bokf && BOKFD_SOCKET=$HOME/bokf-demo/bokfd.sock \
  BOKFD_USER=admin BOKFD_PASSWORD=demo1234 ./build/bokftui`
- Restart daemon: kill the pid file's process, then
  `BOKFD_BACKUP_DIR=$HOME/bokf-demo/backup \
   BOKFD_EXPORT_DIR=$HOME/bokf-demo/export setsid nohup \
   ./build/bokfd --db $HOME/bokf-demo/bokfd.db \
   --socket $HOME/bokf-demo/bokfd.sock > $HOME/bokf-demo/daemon.log 2>&1 &`
- The user's own early instance was `/tmp/x.db` + `/tmp/bokfd.sock`
  (schema v1, old binary) — recreate or migrate it with the current build if
  it is still wanted.
- TUI smoke tests: drive over a pty with `script -qec`; function-key escape
  sequences are timing-sensitive there (not an app bug). `Ctrl+N/C/F` are
  single bytes and reliable. Always wrap the run in
  `scripts/tui-sandbox.sh -- ./build/bokftui ...`: it isolates
  `XDG_CONFIG_HOME`/`XDG_CACHE_HOME` so a test can never overwrite the real
  `~/.config/bokf/tui.conf` or `~/.cache/bokf/tui.log`.

## Known caveats

- Never commit unless the human asks.
- SQLite files must not be backed up live with restic; use
  `backup.snapshot` (`VACUUM INTO`) and point restic at the snapshots.
- Schema version is 3; forward migrations are in `db.c`.