swadhinbiswas # Changelog · 1/17/2026
1464862
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { getDatabase, schema } from "@/db";
import { canReadRepo, canWriteRepo } from "@/lib/permissions";
import { startSSHServer } from "@/lib/ssh";
import { and, eq } from "drizzle-orm";
import { join } from "path";
const DATA_DIR = join(process.cwd(), "data");
const REPOS_DIR = join(DATA_DIR, "repos");
const SSH_DIR = join(DATA_DIR, "ssh");
const HOST_KEY_PATH = join(SSH_DIR, "ssh_host_rsa_key");
async function main() {
console.log("Starting OpenCodeHub SSH Server...");
await startSSHServer({
port: 2222,
hostKeyPath: HOST_KEY_PATH,
reposPath: REPOS_DIR,
authenticateUser: async (_, key) => {
// We ignore the username (usually 'git') and authenticate by key
const db = getDatabase();
// Iterate over all keys to find a match
// In a production app, we would index by fingerprint or key type to optimize
const allKeys = await db.query.sshKeys.findMany();
for (const dbKey of allKeys) {
try {
// Parse DB key
const parts = dbKey.publicKey.trim().split(" ");
if (parts.length < 2) continue;
const dbKeyData = Buffer.from(parts[1], 'base64');
// Compare buffers
if (dbKeyData.equals(key)) {
return {
valid: true,
userId: dbKey.userId,
canRead: true, // Specific repo auth is handled in authorizeRepo
canWrite: true
};
}
} catch (e) {
console.error("Error checking key:", e);
}
}
return { valid: false };
},
authorizeRepo: async (userId, repoPath, operation) => {
// repoPath is "owner/repo.git"
const [ownerName, repoNameWithGit] = repoPath.split("/");
const repoName = repoNameWithGit.replace(".git", "");
if (!ownerName || !repoName) return false;
const db = getDatabase();
// Find Repo
const owner = await db.query.users.findFirst({
where: eq(schema.users.username, ownerName)
});
if (!owner) return false;
const repo = await db.query.repositories.findFirst({
where: and(
eq(schema.repositories.ownerId, owner.id),
eq(schema.repositories.name, repoName)
)
});
if (!repo) return false;
if (operation === "read") {
return canReadRepo(userId, repo);
} else {
return canWriteRepo(userId, repo);
}
},
onPush: async (userId, repoPath, refs) => {
console.log(`Push received to ${repoPath} from ${userId}`);
// Resolve Repo ID
const [ownerName, repoNameWithGit] = repoPath.split("/");
const repoName = repoNameWithGit.replace(".git", "");
const db = getDatabase();
const owner = await db.query.users.findFirst({ where: eq(schema.users.username, ownerName) });
if (!owner) return;
const repo = await db.query.repositories.findFirst({
where: and(eq(schema.repositories.ownerId, owner.id), eq(schema.repositories.name, repoName))
});
if (repo) {
// 1. Run Analysis
import("@/lib/analysis").then(({ analyzeRepository }) => {
analyzeRepository(repo.id, userId).catch(console.error);
});
// 2. Trigger Workflows
import("@/lib/workflows").then(({ triggerRepoWorkflows }) => {
refs.forEach(refLine => {
const [, newSha, refName] = refLine.split(" ");
// Only trigger on branch updates (not deletions or tags for now unless supported)
if (newSha !== "0000000000000000000000000000000000000000" && refName.startsWith("refs/heads/")) {
triggerRepoWorkflows(repo.id, newSha, refName, userId).catch(console.error);
}
});
});
}
}
});
}
main().catch(console.error);