aboutsummaryrefslogtreecommitdiff
path: root/docs/STATE.md
blob: f130f141d2e16293cb6a59697b12bf74810374fd (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
# 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-17.

## 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 posts only
   deltas so nothing is ever edited.
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.

## 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 reviewed starter mapping only; must be
  checked against the current Skatteverket blankett before filing.

## 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.

## 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 2; forward migrations are in `db.c`.