summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-20 10:36:09 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-20 10:36:09 +0200
commit9ee8cb18b1017dce11e7c8c9b7fafad301cb766e (patch)
treeab28600c9c29bb425a25d2375d28e95a97aa1f2c /docs
parentae60b7d2c87cc9ed6878faa57cd4233f77f6f7c3 (diff)
downloadbokf-9ee8cb18b1017dce11e7c8c9b7fafad301cb766e.tar.gz
bokf-9ee8cb18b1017dce11e7c8c9b7fafad301cb766e.zip
db: make attachments append-only (schema v7)
Diffstat (limited to 'docs')
-rw-r--r--docs/SCHEMA.md12
-rw-r--r--docs/STATE.md13
2 files changed, 17 insertions, 8 deletions
diff --git a/docs/SCHEMA.md b/docs/SCHEMA.md
index 89dd0b1..2a85521 100644
--- a/docs/SCHEMA.md
+++ b/docs/SCHEMA.md
@@ -306,6 +306,11 @@ CREATE TABLE attachments (
UNIQUE (org_id, sha256, filename)
) STRICT;
+CREATE TRIGGER attachments_no_update BEFORE UPDATE ON attachments
+BEGIN SELECT RAISE(ABORT, 'attachments are append-only'); END;
+CREATE TRIGGER attachments_no_delete BEFORE DELETE ON attachments
+BEGIN SELECT RAISE(ABORT, 'attachments are append-only'); END;
+
CREATE TABLE voucher_attachments (
org_id INTEGER NOT NULL,
voucher_id INTEGER NOT NULL,
@@ -317,8 +322,11 @@ CREATE TABLE voucher_attachments (
) STRICT;
```
-Attachments are content-addressed and immutable; linking is an insert into
-`voucher_attachments` and is itself audited. Unlinked attachments form the
+Attachments are content-addressed and immutable — the triggers abort updates
+and deletes even for a root `sqlite3` session, and `audit.verify full:true`
+re-hashes the content; linking is an insert into
+`voucher_attachments` and is itself audited (the link table stays mutable so
+underlag can be unlinked). Unlinked attachments form the
inbox the TUI shows. The 7-year archive rule means content must never be
garbage-collected; deduplication by hash keeps repeated receipts cheap.
diff --git a/docs/STATE.md b/docs/STATE.md
index 526c1a6..e210e1c 100644
--- a/docs/STATE.md
+++ b/docs/STATE.md
@@ -172,9 +172,9 @@ check.
and `full:true` re-hashes attachment content; result carries
`vouchers_checked`, `unbalanced_vouchers`, `attachments_checked` and the
first bad voucher/audit/attachment id. TUI Revision shows both counts and
- the bad ids. **Found while testing: `attachments` has no
- append-only triggers** (COMPLIANCE.md §2 claims it does); content changes
- are detected only by `audit.verify full:true`.
+ the bad ids. Fixed in schema v7: `attachments` now has
+ `no_update`/`no_delete` triggers; `audit.verify full:true` still detects
+ on-disk tampering.
5. ~~`report.general_ledger` and `report.voucher_list`~~ implemented
(Huvudbok, Verifikationslista) with Kapitas-style TUI tables; the ledger
API supports `accounts`/`from`/`to`, the list an optional `series`.
@@ -207,7 +207,7 @@ check.
## Environment / how to run
-- **Deployed**: `scripts/deploy.sh` (latest `v0.1.44`, healthy on nas).
+- **Deployed**: `scripts/deploy.sh` (latest `v0.1.48`, healthy on nas).
Live daemon `tls:bokf.makandra.eu:8788`, token
`~/.config/bokf/migration-token` (scopes `read,write`; owner-only actions
like closing years must be done by the human in the TUI). Git remote
@@ -254,8 +254,9 @@ check.
- 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 6 (v3 moms rules; v4/v6 year info; v5 org
- description/shares + board members); forward migrations are in `db.c`.
+- Schema version is 7 (v3 moms rules; v4/v6 year info; v5 org
+ description/shares + board members; v7 attachments append-only triggers);
+ forward migrations are in `db.c`.
## Makandra driftstatus (org 2)