socraticode
Version:
SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.
232 lines • 11.9 kB
JavaScript
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Giancarlo Erra - Altaire Limited
import { execFile } from "node:child_process";
import { randomUUID } from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { z } from "zod/v3";
import { coreProjectId, sanitizeBranchName } from "../config.js";
import { QDRANT_HOST, QDRANT_MODE, QDRANT_URL } from "../constants.js";
const exec = promisify(execFile);
const directorySchema = z.object({
path: z.string().refine(path.isAbsolute),
device: z.string(),
inode: z.string(),
created: z.string(),
}).strict();
const ownershipSchema = z.object({
version: z.literal(1),
ownerId: z.string().uuid(),
identity: z.string(),
projectPath: z.string().refine(path.isAbsolute),
project: directorySchema,
repository: directorySchema,
commonGitDir: directorySchema,
checkoutGitDir: directorySchema,
checkout: directorySchema,
checkoutParent: directorySchema,
branchRef: z.string().regex(/^refs\/heads\/.+/).nullable(),
branchAware: z.boolean(),
linkedWorktree: z.boolean(),
}).strict();
/** Automatic cleanup is off unless the operator explicitly declares local, non-shared use. */
export function automaticCleanupEnabled() {
return process.env.SOCRATICODE_AUTO_CLEANUP === "local";
}
/** The local declaration never enables deletion against an external or non-loopback store. */
export function assertLocalCleanupStore() {
if (!automaticCleanupEnabled())
throw new Error("automatic cleanup is disabled");
const host = QDRANT_URL ? new URL(QDRANT_URL).hostname : QDRANT_HOST;
if (QDRANT_MODE !== "managed" || !["localhost", "127.0.0.1", "::1", "[::1]"].includes(host)) {
throw new Error("automatic cleanup requires host-exclusive managed loopback Qdrant; shared/external stores remain explicit-prune only");
}
if (process.env.SOCRATICODE_PROJECT_ID?.trim())
throw new Error("an explicit project ID is pinned/shared");
}
/** Validate persisted ownership strictly; legacy and malformed records are never adopted. */
export function parseLocalIndexOwnership(value) {
const result = ownershipSchema.safeParse(value);
return result.success ? result.data : null;
}
async function ownerId(create) {
const directory = path.join(os.homedir(), ".socraticode");
const file = path.join(directory, "local-index-owner");
if (create) {
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
try {
await fs.writeFile(file, `${randomUUID()}\n`, { flag: "wx", mode: 0o600 });
}
catch (error) {
if (error.code !== "EEXIST")
throw error;
}
}
const id = (await fs.readFile(file, "utf8")).trim();
if (!z.string().uuid().safeParse(id).success)
throw new Error("local ownership file is invalid; indexes are retained");
return id;
}
async function directoryStamp(directory) {
const canonical = await fs.realpath(directory);
const stat = await fs.stat(canonical, { bigint: true });
if (!stat.isDirectory())
throw new Error(`not a directory: ${directory}`);
return { path: canonical, device: String(stat.dev), inode: String(stat.ino), created: String(stat.birthtimeNs) };
}
async function assertDirectory(original) {
if (JSON.stringify(await directoryStamp(original.path)) !== JSON.stringify(original)) {
throw new Error(`the recorded filesystem directory changed: ${original.path}`);
}
}
async function assertAbsent(directory, refusal) {
try {
await fs.lstat(directory);
}
catch (error) {
if (error.code === "ENOENT")
return;
throw error;
}
throw new Error(refusal);
}
async function git(cwd, ...args) {
try {
const { stdout } = await exec("git", args, { cwd, encoding: "utf8", timeout: 5_000, maxBuffer: 4 * 1024 * 1024, windowsHide: true });
return stdout;
}
catch (error) {
const detail = error;
throw new Error(`Git retirement evidence unavailable: ${detail.killed ? "Git did not respond within 5 seconds" : detail.stderr?.trim() || detail.message}`);
}
}
async function worktrees(cwd) {
const records = (await git(cwd, "worktree", "list", "--porcelain", "-z")).split("\0\0").filter(Boolean);
return records.map((record) => {
const fields = record.split("\0");
const worktree = fields.find((field) => field.startsWith("worktree "))?.slice(9);
if (!worktree || !path.isAbsolute(worktree) || fields.includes("bare"))
throw new Error("Git worktree inventory is ambiguous or bare");
return { path: path.resolve(worktree), branchRef: fields.find((field) => field.startsWith("branch "))?.slice(7) ?? null };
});
}
async function commonGitDirectory(cwd) {
return fs.realpath(path.resolve(cwd, (await git(cwd, "rev-parse", "--git-common-dir")).trim()));
}
async function assertUnpinned(projectPath) {
let raw;
try {
raw = await fs.readFile(path.join(projectPath, ".socraticode.json"), "utf8");
}
catch (error) {
if (error.code === "ENOENT")
return;
throw error;
}
const config = JSON.parse(raw);
if (!config || typeof config !== "object" || Array.isArray(config))
throw new Error("project configuration is ambiguous");
const id = config.projectId;
if (id !== undefined && !(typeof id === "string" && !id.trim()))
throw new Error("project configuration is pinned/shared or ambiguous");
}
function recordedIdentity(ownership) {
const branch = ownership.branchAware && ownership.branchRef ? sanitizeBranchName(ownership.branchRef.slice(11)) : "";
return `${coreProjectId(ownership.projectPath)}${branch ? `__${branch}` : ""}`;
}
async function branchRefs(cwd) {
return (await git(cwd, "for-each-ref", "--format=%(refname)", "refs/heads/")).trim().split("\n").filter(Boolean);
}
function assertNoBranchCollision(ownership, refs) {
if (ownership.branchAware && refs.some((ref) => ref !== ownership.branchRef && recordedIdentity({ ...ownership, branchRef: ref }) === ownership.identity)) {
throw new Error("different branch refs resolve to the recorded identity");
}
}
/** Capture association only for a newly created, explicitly local identity. Failures mean report-only. */
export async function captureLocalIndexOwnership(projectPath, identity) {
assertLocalCleanupStore();
const resolved = path.resolve(projectPath);
await assertUnpinned(resolved);
const checkout = await directoryStamp((await git(resolved, "rev-parse", "--show-toplevel")).trim());
const commonGitDir = await directoryStamp(await commonGitDirectory(resolved));
const registered = await worktrees(resolved);
const primary = registered[0];
const current = registered.find((entry) => entry.path === checkout.path);
if (!primary || !current || await fs.realpath((await git(primary.path, "rev-parse", "--absolute-git-dir")).trim()) !== commonGitDir.path) {
throw new Error("the original repository/worktree association cannot be verified");
}
await assertUnpinned(primary.path);
const branchAware = process.env.SOCRATICODE_BRANCH_AWARE === "true";
const linkedWorktree = current.path !== primary.path;
if (!linkedWorktree && (!branchAware || !current.branchRef || !sanitizeBranchName(current.branchRef.slice(11)))) {
throw new Error("the primary non-branch index is retained");
}
const ownership = {
version: 1, ownerId: await ownerId(true), identity, projectPath: resolved,
project: await directoryStamp(resolved), repository: await directoryStamp(primary.path), commonGitDir,
checkoutGitDir: await directoryStamp((await git(resolved, "rev-parse", "--absolute-git-dir")).trim()), checkout,
checkoutParent: await directoryStamp(path.dirname(checkout.path)),
branchRef: current.branchRef,
branchAware, linkedWorktree,
};
if (recordedIdentity(ownership) !== identity)
throw new Error("the Git branch no longer matches the index identity");
if (ownership.project.device !== checkout.device || checkout.device !== ownership.checkoutParent.device) {
throw new Error("a mount boundary prevents proving checkout removal safely");
}
if (ownership.linkedWorktree && (path.relative(commonGitDir.path, ownership.checkoutGitDir.path).startsWith("..") || ownership.checkoutGitDir.device !== commonGitDir.device)) {
throw new Error("the worktree administration directory is outside the original Git filesystem");
}
assertNoBranchCollision(ownership, await branchRefs(primary.path));
const ref = (await git(resolved, "rev-parse", "--symbolic-full-name", "HEAD")).trim();
if ((ref === "HEAD" ? null : ref) !== ownership.branchRef || await commonGitDirectory(resolved) !== commonGitDir.path) {
throw new Error("the Git checkout changed during ownership capture");
}
return ownership;
}
/** Prove actual Git retirement. Filesystem absence alone, merges and switches never suffice. */
export async function assertLocalIndexRetired(ownership) {
assertLocalCleanupStore();
if (await ownerId(false) !== ownership.ownerId)
throw new Error("the index belongs to another local installation");
if (recordedIdentity(ownership) !== ownership.identity)
throw new Error("the ownership record does not match its identity");
if (ownership.linkedWorktree !== (ownership.checkout.path !== ownership.repository.path))
throw new Error("the recorded worktree association is inconsistent");
await assertDirectory(ownership.repository);
await assertDirectory(ownership.commonGitDir);
await assertDirectory(ownership.checkoutParent);
await assertUnpinned(ownership.repository.path);
if (await commonGitDirectory(ownership.repository.path) !== ownership.commonGitDir.path)
throw new Error("the original Git repository changed");
const registered = await worktrees(ownership.repository.path);
if (registered[0]?.path !== ownership.repository.path)
throw new Error("the original primary Git checkout changed");
const refs = await branchRefs(ownership.repository.path);
assertNoBranchCollision(ownership, refs);
const stillRegistered = registered.some((entry) => entry.path === ownership.checkout.path);
if (ownership.linkedWorktree && !stillRegistered) {
await assertAbsent(ownership.checkoutGitDir.path, "the original worktree Git directory still exists; the worktree may have moved");
await assertDirectory(ownership.commonGitDir);
await fs.readdir(ownership.commonGitDir.path);
await assertAbsent(ownership.checkout.path, "the unregistered worktree directory still exists");
await assertDirectory(ownership.checkoutParent);
await fs.readdir(ownership.checkoutParent.path);
return;
}
if (!stillRegistered)
throw new Error("the original checkout is not registered; retirement is ambiguous");
await assertDirectory(ownership.checkout);
await assertDirectory(ownership.checkoutGitDir);
await assertDirectory(ownership.project);
await assertUnpinned(ownership.projectPath);
if (await commonGitDirectory(ownership.projectPath) !== ownership.commonGitDir.path)
throw new Error("the indexed directory belongs to another Git repository");
if (!ownership.branchAware || !ownership.branchRef || refs.includes(ownership.branchRef))
throw new Error("the indexed branch/worktree is still registered");
if (registered.some((entry) => entry.branchRef === ownership.branchRef))
throw new Error("a registered worktree still uses the indexed branch");
}
//# sourceMappingURL=local-index-ownership.js.map