package main import ( "crypto/rand" "crypto/sha256" "database/sql" "encoding/base64" "encoding/hex" "time" _ "modernc.org/sqlite" ) // DB holds the relay's only state: the set of per-instance bearer tokens the // admin has minted. It stores NO push traffic — no device tokens, threads, or // blobs ever touch disk. Tokens are stored hashed (cf. the messenger's admin // token), so a stolen DB yields no live credential. type DB struct{ sql *sql.DB } func openDB(path string) (*DB, error) { d, err := sql.Open("sqlite", path) if err != nil { return nil, err } // One writer at a time keeps WAL happy and avoids "database is locked". d.SetMaxOpenConns(1) for _, p := range []string{ "PRAGMA journal_mode=WAL", "PRAGMA busy_timeout=5000", "PRAGMA synchronous=NORMAL", } { if _, err := d.Exec(p); err != nil { return nil, err } } db := &DB{sql: d} if err := db.migrate(); err != nil { return nil, err } return db, nil } func (db *DB) migrate() error { _, err := db.sql.Exec(` CREATE TABLE IF NOT EXISTS relay_tokens ( id TEXT PRIMARY KEY, -- random public handle, used to revoke label TEXT NOT NULL, -- e.g. "family.example.com" — for logs/listing, not auth token_hash TEXT NOT NULL UNIQUE, -- sha256(bearer token); the secret itself is never stored created_at INTEGER NOT NULL, last_used_at INTEGER );`) return err } func hashToken(t string) string { h := sha256.Sum256([]byte(t)) return hex.EncodeToString(h[:]) } func randToken() (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err } return base64.RawURLEncoding.EncodeToString(b), nil } // RelayToken is a token row for the admin listing. It deliberately never carries // the secret (only mintToken ever returns that, once). type RelayToken struct { ID string `json:"id"` Label string `json:"label"` CreatedAt int64 `json:"created_at"` LastUsedAt *int64 `json:"last_used_at,omitempty"` } // mintToken creates a per-instance token, stores only its hash, and returns the // plaintext ONCE (the caller shows it to the operator; it's unrecoverable after). func (db *DB) mintToken(label string) (id, plaintext string, err error) { if id, err = randToken(); err != nil { return "", "", err } if plaintext, err = randToken(); err != nil { return "", "", err } _, err = db.sql.Exec( `INSERT INTO relay_tokens(id, label, token_hash, created_at) VALUES(?, ?, ?, ?)`, id, label, hashToken(plaintext), time.Now().Unix(), ) if err != nil { return "", "", err } return id, plaintext, nil } // authInstance resolves a bearer token to its instance label, bumping // last_used_at. Returns ""/false for an empty or unknown token. func (db *DB) authInstance(token string) (label string, ok bool) { if token == "" { return "", false } var id string err := db.sql.QueryRow( `SELECT id, label FROM relay_tokens WHERE token_hash = ?`, hashToken(token), ).Scan(&id, &label) if err != nil { return "", false } // Best-effort; a failed bump doesn't deny an otherwise-valid token. _, _ = db.sql.Exec(`UPDATE relay_tokens SET last_used_at = ? WHERE id = ?`, time.Now().Unix(), id) return label, true } func (db *DB) listTokens() ([]RelayToken, error) { rows, err := db.sql.Query( `SELECT id, label, created_at, last_used_at FROM relay_tokens ORDER BY created_at`, ) if err != nil { return nil, err } defer rows.Close() out := []RelayToken{} for rows.Next() { var t RelayToken var last sql.NullInt64 if err := rows.Scan(&t.ID, &t.Label, &t.CreatedAt, &last); err != nil { return nil, err } if last.Valid { t.LastUsedAt = &last.Int64 } out = append(out, t) } return out, rows.Err() } func (db *DB) revokeToken(id string) (bool, error) { res, err := db.sql.Exec(`DELETE FROM relay_tokens WHERE id = ?`, id) if err != nil { return false, err } n, _ := res.RowsAffected() return n > 0, nil }