# bokf compliance notes Status: Draft 0.1 · 2026-09-17 · License: GPL-3.0-or-later This document maps Swedish bookkeeping requirements to concrete `bokf` features, and states plainly what the software does **not** do. It is a design document, not legal advice. Statutory references are indicative — verify the current wording, and engage a redovisningskonsult or revisor before relying on any of it. The short version: no Swedish authority certifies bookkeeping software. Compliance is always the responsibility of the bokföringsskyldige. `bokf`'s goal is to make the formal requirements hard to violate by construction: append-only data, enforced number series, verifiable history and locked periods. ## 1. Framework | Source | What it governs | |---|---| | Bokföringslagen (1999:1078), BFL | Bookkeeping duty, löpande bokföring, verifikationer, bevarande | | BFNAR 2013:2, vägledning om räkenskapsinformation | Systemdokumentation, behandlingshistorik, rättelser, säkerhet | | Årsredovisningslagen (1995:1554), ÅRL | Årsredovisning, Bolagsverket filing | | BFNAR 2016:10 (K2), BFNAR 2012:1 (K3) | Accounting frameworks for AB | | Mervärdesskattelagen (2023:200), ML | Moms reporting | | SIE 4 | De facto exchange standard between systems, banks, accountants, auditors | | BAS-kontoplanen (FAR) | Standard chart of accounts; separately attributed seed data | | GDPR | Personal data in the ledger and in user management | ## 2. Requirement → feature map | Requirement | Where in bokf | Status | |---|---|---| | **Löpande bokföring** (BFL 4 kap) — entries made continuously, in Swedish kronor, in chronological order | Every voucher has a date within its fiscal year; listings are ordered by date, series, number; server rejects out-of-range dates | MVP | | **Verifikationer** (BFL 5 kap) — every entry has a dated verification with underlag, amount, counterpart, and identifies the counterparty | `voucher.post` requires description, ≥ 2 balanced rows, account references, actor identity; underlag via immutable `attachments` linked to the voucher | MVP | | **Obruten nummerserie** per räkenskapsår | `sequences` incremented in the same transaction as the insert; a failed post consumes nothing, a committed post skips nothing; `UNIQUE(org, year, series, number)` | MVP | | **Oföränderlighet** — a verifikation may not be altered after the fact | `BEFORE UPDATE/DELETE` triggers abort on vouchers, rows, attachments and audit. No application command can change ledger rows | MVP | | **Rättelser** — errors corrected by a new verifikation that references the old (ändringsverifikat), never by editing | `voucher.correct` mirrors and posts a new voucher carrying `corrects_voucher_id`; original remains visible with both linked in reports | MVP | | **Behandlingshistorik** (BFNAR 2013:2) — who did what, when, with what result | `audit_log`, append-only, SHA-256 chained, one entry per mutation with actor user **and** token; `audit.verify` recomputes the chain | MVP | | **Systemdokumentation** (BFNAR 2013:2) | `docs/` (this file, `PROTOCOL.md`, `SCHEMA.md`), `meta.schema_version`, runtime `describe`; the operator must keep it current for their installation | MVP, operator duty | | **Bevarande i 7 år** (BFL 7 kap) | Append-only storage; no delete commands for ledger data; `archive`/disable instead of delete; `backup.snapshot` + restic pattern produces dated, restorable archives | MVP, operator duty | | **Tillgänglighet och läsbarhet** | JSON reports, SIE 4 export in CP437, SIE import; the archive is readable by any tool that reads SQLite or SIE | MVP | | **Huvudbok, saldon** (god redovisningssed) | `report.general_ledger`, `report.trial_balance`, `report.balance_sheet`, `report.income_statement` | MVP | | **Moms** (ML 2023:200) | `report.vat` computes the boxes from `report_rules`; declaration is filed with Skatteverket by the operator | MVP in-system; eSKD file generation roadmap | | **SIE 4** | `sie.export` (PC8/CP437) and `sie.import` (migration from Fortnox/Visma/BL) | MVP | | **Årsredovisning / INK2 / SRU** (ÅRL, K2) | Reports supply the figures; document generation and SRU files are roadmap | Roadmap | | **AGI, arbetsgivaravgifter, löner** | Not present | Roadmap | | **Fakturering, kund- och leverantörsreskontra** | Not present | Roadmap | | **Kontrolluppgifter, periodiska sammanställningar** | Not present | Roadmap / manual | | **GDPR** — lawful basis for ledger personal data, retention overrides erasure | Role-based access, revocable tokens, audit trail, local hosting; see §5 | MVP in-system, operator duty | ## 3. How invariants are enforced The interesting detail is that compliance here is not a feature that can be switched off, or bypassed by a clever client: 1. **The server owns the invariants.** Clients — including agents — cannot construct a voucher that skips a number, exceeds a lock, or references another org's account. 2. **The database enforces tenancy.** Composite keys make cross-org references impossible at the storage layer (`SCHEMA.md` §1, §4–7). 3. **The database enforces immutability.** Triggers abort updates and deletes on ledger and audit tables. This also holds for manual `sqlite3` access as root. 4. **History is verifiable.** Voucher and audit chains are recomputable by `audit.verify`; tampering is detectable, not just forbidden. 5. **Locks are explicit.** `period.lock` is owner-only and every unlock is logged with a reason. `fiscal_year.close` is irreversible from the API. ## 4. Verifikationer and the correction flow - A voucher = verifikation: unique number in an unbroken series, date, description, rows, creator, timestamp, hash, linked underlag. - Corrections per BFNAR 2013:2: post an ändringsverifikat with `voucher.correct`, referencing the original. Both remain in the ledger and in every report; the link is machine-readable. - Deleting or hiding a posted voucher is not possible; there is no command for it and no SQL path to it under normal operation. - Underlag (receipts, invoices) live in the same database as immutable blobs, linked to exactly the vouchers they support. Missing-underlag is a first-class query (`attachment.list {unlinked:...}` and per-voucher coverage). ## 5. Data location, backups and the 7-year archive - Plan to host within the EES and keep the data available for inspection. BFL's rules on where räkenskapsinformation may be kept and how it must be presentable are precise — check the current 7 kap. wording for your case. `bokf` makes no transfers anywhere; there is no telemetry and no cloud dependency. - **Never back up live SQLite files with a file-copy tool.** Use `backup.snapshot` (`VACUUM INTO`), which produces a consistent single file with no downtime, then let restic pick that up. Exclude the live `*.db`/`-wal`/`-shm` from restic. - Test restores on a schedule; a backup that has never been restored is not an archive. The restore procedure is also the retrieval path for inspections. - 7 years = seven years after the calendar year in which the fiscal year ended. Restic retention is not a legal retention policy by itself: keep at least annual snapshots for the full period and store them somewhere you can still read in a decade. ## 6. Roles, audit and access | Actor | Typical holder | Sees | |---|---|---| | `owner` | You | Everything, including unlocks, closing and member management | | `bookkeeper` | Accounting help | Books and reports, posts and corrects, no irreversible ops | | `viewer` | Revisor, auditor, board member | Read-only, including audit history | | token | Agent or integration | Narrowed scope, its own audit identity, revocable instantly | Every mutation records the acting user **and** token, so machine-written entries are attributable to the machine and to the human who authorized it. Passwords and tokens never reach the audit log or the application log. ## 7. GDPR notes - Ledger personal data (customer/supplier names, employee data later) is processed under legal obligation (BFL), not consent; retention periods override erasure requests. A data subject request must be answered within those constraints, not by deleting bookkeeping. - User accounts (name, username, password hash, token metadata) are kept to operate the service; disable instead of delete when history references them. - Because everything is single-host, data subject access requests can be answered from reports and audit listings; there is no third party to notify. - Keep the host physically and network secured: LUKS at rest, no public listener by default, tokens revocable, sessions short-lived. ## 8. What the operator still must do The software does not file anything, sign anything, or know your business: - File momsdeklaration, AGI, INK2 and other declarations with Skatteverket (upload files once the eSKD/SRU roadmap items land, or via the e-service). - Prepare and sign the årsredovisning and file it with Bolagsverket; K2 generation is roadmap, and the figures still need review. - Keep a current systemdokumentation for the installation (hardware, OS, backup, access) — `docs/` is the starting point. - Decide the moms period, fiscal year and accounting framework per org (`org.create` args) and keep them correct. - Test restores; hold two independent backup copies. - Have a redovisningskonsult or revisor review at least the first year. - Remember that only humans can exercise judgement: VAT treatment, fringe benefits, cut-off, going concern. ## 9. Honest status This is a draft specification. Nothing here has been audited, and the system has no production history yet. Treat generated reports and declaration figures as you would any new tool's output: review before filing. The compliance value of `bokf` rests on the enforced invariants above, not on approval by any authority — none exists. ## 10. Roadmap, in rough order 1. Ledger core + protocol + CLI + TUI (MVP). 2. SIE round-trip test suite and migration dry-run against files exported from Fortnox/Visma/BL. 3. eSKD file for momsdeklaration; AGI file for payroll. 4. K2 årsredovisning document generation + SRU files for INK2. 5. Invoicing and reskontra; bank import (CSV, then PSD2). 6. Peppol e-invoicing, if still relevant when it is due.