Files
zerobyte/app/server/core/capabilities.ts
iven aed2c15709 Rename docker-compose.yml to compose.yaml (#935)
Aligns the project with the current Compose Specification, which
designates compose.yaml as the canonical filename and treats the
docker-compose.yml name as a legacy fallback.

Renames every compose file in the repo (the root dev/e2e stack, the
deployment examples under examples/, and the integration-test infra
stack) and updates all documentation, the integration test runner, the
capability hint messages, and the .gitattributes pattern accordingly.
No top-level version field was present to remove.

Functional behavior is unchanged: docker compose discovers either
filename, so existing deployments are not affected by the rename.

Reference: https://docs.docker.com/compose/intro/compose-application-model/
2026-06-10 20:04:00 +02:00

93 lines
2.6 KiB
TypeScript

import * as fs from "node:fs/promises";
import { RCLONE_CONFIG_DIR, RCLONE_CONFIG_FILE } from "./constants";
import { logger } from "@zerobyte/core/node";
type SystemCapabilities = {
rclone: boolean;
sysAdmin: boolean;
};
let capabilitiesPromise: Promise<SystemCapabilities> | null = null;
/**
* Returns the current system capabilities.
* On first call, detects all capabilities and caches the promise.
* Subsequent calls return the same cached promise, ensuring detection only happens once.
*/
export async function getCapabilities(): Promise<SystemCapabilities> {
if (capabilitiesPromise === null) {
// Start detection and cache the promise
capabilitiesPromise = detectCapabilities();
}
return capabilitiesPromise;
}
/**
* Detects which optional capabilities are available in the current environment
*/
async function detectCapabilities(): Promise<SystemCapabilities> {
return {
rclone: await detectRclone(),
sysAdmin: await detectSysAdmin(),
};
}
/**
* Checks if rclone is available by:
* 1. Checking if the rclone config file exists and is accessible
*/
async function detectRclone(): Promise<boolean> {
try {
await fs.access(RCLONE_CONFIG_FILE);
logger.info("rclone capability: enabled");
return true;
} catch (_) {
logger.warn(
`rclone capability: disabled. ` + `To enable: mount rclone config at ${RCLONE_CONFIG_DIR} in compose.yaml`,
);
return false;
}
}
async function detectSysAdmin(): Promise<boolean> {
if (process.platform !== "linux") {
logger.warn("sysAdmin capability: disabled. Non-Linux platform detected");
return false;
}
try {
const procStatus = await fs.readFile("/proc/self/status", "utf-8");
const capEffLine = procStatus.split("\n").find((line) => line.startsWith("CapEff:"));
if (!capEffLine) {
logger.warn("sysAdmin capability: disabled. Could not read CapEff from /proc/self/status");
return false;
}
// Extract the hex value (e.g., "00000000a80425fb")
const capEffHex = capEffLine.split(/\s+/)[1];
if (!capEffHex) {
logger.warn("sysAdmin capability: disabled. Could not parse CapEff value");
return false;
}
// Check if bit 21 (CAP_SYS_ADMIN) is set
const capValue = parseInt(capEffHex, 16) & (1 << 21);
if (capValue !== 0) {
logger.info("sysAdmin capability: enabled (CAP_SYS_ADMIN detected)");
return true;
}
logger.warn("sysAdmin capability: disabled. " + "To enable: add 'cap_add: SYS_ADMIN' in compose.yaml");
return false;
} catch (_error) {
logger.warn("sysAdmin capability: disabled. " + "To enable: add 'cap_add: SYS_ADMIN' in compose.yaml");
return false;
}
}