oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1package home
2
3import (
4 "context"
5 "sync"
6
7 "modernc.org/sqlite"
8)
9
10// secureDeleteOnce guards the connection hook registration. The hook
11// list is process-global — sqlite.RegisterConnectionHook appends to the
12// package's single driver — so registering per call would run the pragma
13// once per registration on every new connection, forever.
14var secureDeleteOnce sync.Once
15
16// RegisterSecureDelete turns on PRAGMA secure_delete for every
17// connection this process opens.
18//
19// It matters because deleting a row does not remove its bytes from the
20// file (§5.1). What the pragma actually does, stated precisely because
21// an earlier version of this comment overstated it: SQLite zeroes freed
22// content in the pages it rewrites, so the page that lands in the main
23// file after a checkpoint, and the frame written for that change, do not
24// carry the deleted bytes.
25//
26// What it does NOT do: recall anything. A frame Litestream has already
27// shipped is gone from our reach, and measurement confirms the deleted
28// value is still readable in the local -wal until a checkpoint. The
29// control narrows the window; it does not close it. That is why the
30// column is sealed as well — the bytes that do survive are bytes nobody
31// can read.
32//
33// Three things make this the shape it is, and each was a way to get it
34// wrong:
35//
36// The pragma is PER CONNECTION. An Exec after opening sets it on
37// whichever pooled connection served that call and on no other, and on
38// none opened later. A test that reads the pragma back on that same
39// connection passes while proving nothing.
40//
41// rastrillo/db.Open builds its own DSN — busy_timeout, journal_mode and
42// foreign_keys — and passes nothing else through, so there is no way to
43// add secure_delete as a DSN pragma without changing rastrillo. Hence
44// the driver hook.
45//
46// It must be registered BEFORE db.Open. Open calls Ping, which opens the
47// writer pool's single long-lived connection there and then; a hook
48// registered afterwards misses that connection forever, and it is the
49// only one that performs DELETEs.
50// A neighbouring hazard, recorded here because this is where somebody
51// will look: rastrillo/db.Open installs a GORM logger that prints slow
52// queries — over 200ms — WITH THEIR PARAMETERS INTERPOLATED, at Warn.
53// Nothing in this app sends an address through d.G today; everything
54// address-bearing goes through Writer() with bound parameters. The first
55// query that does go through GORM with an address in it will put that
56// address into platform-held logs on any slow night.
57func RegisterSecureDelete() {
58 secureDeleteOnce.Do(func() {
59 sqlite.RegisterConnectionHook(func(conn sqlite.ExecQuerierContext, _ string) error {
60 _, err := conn.ExecContext(context.Background(), "PRAGMA secure_delete=ON", nil)
61 return err
62 })
63 })
64}
65