OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git commits / 34fef41

Auto-announce new repos on DHT at registration time
Root cause: repos registered at runtime (import, fast-scan, GitSync _initBareRepo) were never announced on the DHT. Federation peers couldn't fetch via punch:// because the topic wasn't being served. Immediate fix: - handleImport announces after importing - handleFastScan announces newly discovered repos Long-term fix: - RepoRegistry.register() has onRepoRegistered callback - Server wires it to topicManager.announceRepo after P2P init - ANY code path that registers a new repo gets DHT announcement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
34fef41bf600645db680f6d18150d6de4bd9795f
by plankatron <fleeky@gmail.com> · 2026-04-07T02:19:16.000Z

Changed files (3) +30 -0

lib/repo-registry.js +11 -0
……@@ -14,6 +14,7 @@ export class RepoRegistry {
1414 constructor(options = {}) {
1515 this.registry = options.registry // Hyperbee instance
1616 this.gitReposPath = options.gitReposPath || path.join(os.homedir(), 'git-repos')
17 this.onRepoRegistered = options.onRepoRegistered || null // callback(repoName) — called on NEW registrations
1718 }
1819
1920 /**
……@@ -481,6 +482,7 @@ echo "[post-receive] Auto-deploy triggered for $REPO_NAME"
481482
482483 // Merge with existing entry to preserve user-configured fields
483484 const existing = await this.registry.get(`repo:${repoInfo.name}`).catch(() => null)
485 const isNew = !existing || !existing.value
484486 if (existing && existing.value) {
485487 const prev = existing.value
486488 // Preserve fields that are set via separate APIs (not from scan).
……@@ -523,6 +525,15 @@ echo "[post-receive] Auto-deploy triggered for $REPO_NAME"
523525 }
524526
525527 console.log(` ✓ Registered ${repoInfo.name}${repoInfo.namespace ? ` (${repoInfo.namespace})` : ''}`)
528
529 // Notify listener on NEW registrations (not re-registrations)
530 if (isNew && this.onRepoRegistered) {
531 try {
532 await this.onRepoRegistered(repoInfo.name)
533 } catch (err) {
534 console.error(` ⚠ onRepoRegistered callback failed for ${repoInfo.name}: ${err.message}`)
535 }
536 }
526537 }
527538
528539 /**
lib/routes/repos.js +17 -0
……@@ -149,6 +149,8 @@ async function handleScan(req, res, server) {
149149async function handleFastScan(req, res, server) {
150150 const { repoRegistry } = server.repoData
151151 try {
152 const previousNames = new Set(server.repos.map(r => r.name))
153
152154 const repos = await repoRegistry.scanAndRegister({
153155 paths: [repoRegistry.gitReposPath],
154156 maxDepth: 1
……@@ -156,6 +158,15 @@ async function handleFastScan(req, res, server) {
156158
157159 server.repos = repos
158160
161 // Announce any newly discovered repos on DHT
162 const topicManager = server.p2p?.topicManager
163 if (topicManager) {
164 const newRepos = repos.filter(r => !previousNames.has(r.name))
165 for (const r of newRepos) {
166 await topicManager.announceRepo(r.name)
167 }
168 }
169
159170 json(res, {
160171 success: true,
161172 count: repos.length,
……@@ -188,6 +199,12 @@ async function handleImport(req, res, server) {
188199 server.repos.push(entry)
189200 }
190201
202 // Announce on DHT so federation peers can fetch via punch://
203 const topicManager = server.p2p?.topicManager
204 if (topicManager) {
205 await topicManager.announceRepo(repoName)
206 }
207
191208 json(res, {
192209 success: true,
193210 driveKey: imported.driveKey,
pear-git-server.js +2 -0
……@@ -1176,6 +1176,8 @@ class PearGitServer {
11761176 this.gitP2PHandler.topicManager = this.topicManager
11771177 // Set topicManager on GitSync client (needed for announcing runtime-created repos)
11781178 if (this.gitSyncClientManager) this.gitSyncClientManager._topicManager = this.topicManager
1179 // Auto-announce new repos on DHT when registered at runtime
1180 this.repoRegistry.onRepoRegistered = (repoName) => this.topicManager.announceRepo(repoName)
11791181 // Set server ref so admin create-repo can update in-memory repos cache
11801182 this.gitP2PHandler.server = this
11811183