feat: add Postgres store with append-only schema and migrations

internal/store connects via pgx and runs golang-migrate migrations
embedded in the binary (go:embed), so Deklarix stays a single binary
despite the move to Postgres. Schema covers the five MVP tables
(submission, asset, extraction, finding, evidence_package, participant).

extraction, finding and evidence_package are append-only by design: a
Postgres trigger rejects UPDATE/DELETE outright, since a corrigible
evidence archive isn't an evidence archive. Corrections to a finding are
new rows whose supersedes column points at the row they replace (set at
INSERT time on the new row, since the trigger blocks UPDATE on the old
one) — "currently valid" findings are the ones no other row supersedes.

scripts/test.sh now spins up a disposable Postgres container so the
store's integration tests (including the append-only guarantee) actually
run on every test.sh/release.sh invocation instead of silently skipping
for lack of DATABASE_URL.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
noroot
2026-08-27 00:50:30 +02:00
parent bfe52c73eb
commit e9e386df85
9 changed files with 347 additions and 0 deletions

View File

@@ -1,13 +1,31 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"github.com/netcell-it/deklarix/internal/store"
)
func main() {
databaseURL := os.Getenv("DATABASE_URL")
if databaseURL == "" {
log.Fatal("DATABASE_URL is required")
}
if err := store.Migrate(databaseURL); err != nil {
log.Fatalf("migrate: %v", err)
}
db, err := store.Open(context.Background(), databaseURL)
if err != nil {
log.Fatalf("open store: %v", err)
}
defer db.Close()
port := os.Getenv("PORT")
if port == "" {
port = "8080"