Move domain config from .env to a private SQLite store

Forges (multi-host) + tokens, project directories, and git identity now live in a private SQLite config store (internal/store, modernc.org/sqlite) on a /data named volume that is not bind-mounted or exposed, so credentials aren't reachable outside the container. New Settings page (/settings) + <settings-panel> with /api/config CRUD. Scanner reads roots fresh from the store each cycle; service resolves forges per-repo from the store and reapplies per-forge git auth on change. First run seeds the store from .env. Overturns the old no-datastore/.env-config laws (AGENT.md updated). Verified live end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 05:31:28 -04:00
parent 69d38484e8
commit e30c3b632a
18 changed files with 1208 additions and 160 deletions
+10 -2
View File
@@ -17,6 +17,7 @@ import (
"gitmanager/internal/git"
"gitmanager/internal/repos"
"gitmanager/internal/service"
"gitmanager/internal/store"
)
// TestMCPRoundTrip exercises the full path: a real temp git repo -> scanner ->
@@ -39,10 +40,17 @@ func TestMCPRoundTrip(t *testing.T) {
// Populate the index via the real scanner, then build service + MCP server.
log := slog.New(slog.NewTextHandler(io.Discard, nil))
g := git.New("git")
scanner := repos.NewScanner(g, log, []string{root}, 3, nil, time.Minute, false)
scanner := repos.NewScanner(g, log, func(context.Context) repos.Config {
return repos.Config{Roots: []string{root}, MaxDepth: 3, Fetch: false}
}, time.Minute)
scanner.Refresh(context.Background())
svc := service.New(g, scanner.Index, activity.New(log, 200), nil, scanner.RefreshRepo)
st, err := store.Open(filepath.Join(t.TempDir(), "config.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
svc := service.New(g, scanner.Index, activity.New(log, 200), st, log, scanner.RefreshRepo)
srv := NewServer(svc, "test")
// Wire an in-memory client<->server session.