From b907bf93ec36e1fab5eee9365ba85922e89a5d2f Mon Sep 17 00:00:00 2001 From: "kppcn.tngl.sh" Date: Wed, 8 Apr 2026 06:57:31 +0000 Subject: [PATCH] knotserver/xrpc: defer plc submission until repo setup succeeds Previously, CreateRepo submitted the PLC DID before the remaining local setup steps had completed. If RBAC setup or hook installation failed after that point, the handler cleaned up local state but still left behind a published DID with no corresponding repo on disk. Move PLC submission to the end of the create flow so the DID is only published after local repo setup succeeds. Also roll back repo RBAC state during cleanup, and treat hook setup failure as fatal instead of silently continuing. --- knotserver/xrpc/create_repo.go | 38 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/knotserver/xrpc/create_repo.go b/knotserver/xrpc/create_repo.go index 7701d5b5..813d7de2 100644 --- a/knotserver/xrpc/create_repo.go +++ b/knotserver/xrpc/create_repo.go @@ -145,6 +145,7 @@ func (h *Xrpc) CreateRepo(w http.ResponseWriter, r *http.Request) { repoPath, _ := securejoin.SecureJoin(h.Config.Repo.ScanPath, repoDid) rbacPath := repoDid + repoAddedToRBAC := false cleanup := func() { if rmErr := os.RemoveAll(repoPath); rmErr != nil { @@ -153,6 +154,11 @@ func (h *Xrpc) CreateRepo(w http.ResponseWriter, r *http.Request) { } cleanupAll := func() { + if repoAddedToRBAC { + if rmErr := h.Enforcer.RemoveRepo(actorDid.String(), rbac.ThisServer, rbacPath); rmErr != nil { + l.Error("failed to clean up repo permissions", "error", rmErr.Error()) + } + } cleanup() if delErr := h.Db.DeleteRepoKey(repoDid); delErr != nil { l.Error("failed to clean up repo key", "error", delErr.Error()) @@ -195,17 +201,6 @@ func (h *Xrpc) CreateRepo(w http.ResponseWriter, r *http.Request) { } } - if prepared != nil { - plcCtx, plcCancel := context.WithTimeout(context.Background(), 30*time.Second) - defer plcCancel() - if err := prepared.Submit(plcCtx); err != nil { - l.Error("submitting to PLC directory", "error", err.Error()) - cleanupAll() - writeError(w, xrpcerr.GenericError(fmt.Errorf("PLC directory submission failed: %w", err)), http.StatusInternalServerError) - return - } - } - // add perms for this user to access the repo err = h.Enforcer.AddRepo(actorDid.String(), rbac.ThisServer, rbacPath) if err != nil { @@ -214,14 +209,31 @@ func (h *Xrpc) CreateRepo(w http.ResponseWriter, r *http.Request) { writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError) return } + repoAddedToRBAC = true - hook.SetupRepo( + if err := hook.SetupRepo( hook.Config( hook.WithScanPath(h.Config.Repo.ScanPath), hook.WithInternalApi(h.Config.Server.InternalListenAddr), ), repoPath, - ) + ); err != nil { + l.Error("setting up repo hooks", "error", err.Error()) + cleanupAll() + writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError) + return + } + + if prepared != nil { + plcCtx, plcCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer plcCancel() + if err := prepared.Submit(plcCtx); err != nil { + l.Error("submitting to PLC directory", "error", err.Error()) + cleanupAll() + writeError(w, xrpcerr.GenericError(fmt.Errorf("PLC directory submission failed: %w", err)), http.StatusInternalServerError) + return + } + } // HACK: request crawl for this repository // Users won't want to sync entire network from their local knotmirror. -- 2.51.2