Artifact c937ed6c1a605addbcc4b8a8506dc3c3e6f1ede8b01f621b1fef76d4bfb9da67:
- File
internal/oltp/oltp.go
— part of check-in
[2c157378a3]
at
2026-08-18 01:24:41
on branch trunk
— repo: add and remove repositories without restarting
Adding a repository meant editing the server's command line and restarting, which took every other repository down with it. The registry now lives in OLTP where 0001 §8 always placed it, and repo.add / repo.remove (admin) register and tear down at runtime.
registrar owns a repository's whole lifetime — index handle, pipeline, sync backend, poll goroutine. The startup loop could not: it relied on defer ix.Close() reaching serve's return and on a poll loop that never stops, and both leak the moment a repository is removed.
Add persists before it serves, and rolls the record back if opening fails: a registration that serves without being recorded vanishes at the next restart with no trace of why. Remove stops serving and forgets, but leaves the store and index on disk — 'stop serving' and 'destroy' are different intentions and only one is reversible.
--repo stays as bootstrap and override. (user: dmestas, size: 2701) [annotate] [blame] [check-ins using]
// Package oltp is quarry's forge OLTP database (0001 §8): the only // authoritative non-DAG state. v1 holds sync users — per-repo login // credentials for push gating. Small by construction; the litestream // replica is the recovery story when deployment warrants it. package oltp import ( "database/sql" "fmt" "github.com/ncruces/go-sqlite3/driver" ) const schema = ` CREATE TABLE IF NOT EXISTS sync_user ( repo TEXT NOT NULL, user TEXT NOT NULL, secret TEXT NOT NULL, -- RFC §6.3 shared secret (40 hex); plaintext never stored PRIMARY KEY (repo, user) );` type DB struct { db *sql.DB } func Open(path string) (*DB, error) { db, err := driver.Open("file:" + path) if err != nil { return nil, err } if _, err := db.Exec(`PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;`); err != nil { db.Close() return nil, fmt.Errorf("oltp: pragmas: %w", err) } if _, err := db.Exec(schema); err != nil { db.Close() return nil, fmt.Errorf("oltp: schema: %w", err) } if _, err := db.Exec(accountSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: account schema: %w", err) } if _, err := db.Exec(webhookSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: webhook schema: %w", err) } if _, err := db.Exec(sessionSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: session schema: %w", err) } if _, err := db.Exec(ciSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: ci schema: %w", err) } if _, err := db.Exec(mirrorSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: mirror schema: %w", err) } if _, err := db.Exec(repoSchema); err != nil { db.Close() return nil, fmt.Errorf("oltp: repo schema: %w", err) } return &DB{db: db}, nil } func (d *DB) Close() error { return d.db.Close() } // SetSyncUser stores (replacing) one repo user's shared secret. func (d *DB) SetSyncUser(repo, user, secret string) error { _, err := d.db.Exec(`INSERT OR REPLACE INTO sync_user (repo, user, secret) VALUES (?, ?, ?)`, repo, user, secret) return err } // Secret returns the stored shared secret for one repo user. func (d *DB) Secret(repo, user string) (string, bool) { var s string err := d.db.QueryRow(`SELECT secret FROM sync_user WHERE repo = ? AND user = ?`, repo, user).Scan(&s) return s, err == nil } // Gated reports whether a repo has any sync users — the v1 policy // switch: no users means anonymous push stays allowed (local use), // one or more users means pushes must authenticate. func (d *DB) Gated(repo string) bool { var n int if err := d.db.QueryRow(`SELECT count(*) FROM sync_user WHERE repo = ?`, repo).Scan(&n); err != nil { return false } return n > 0 }