Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -872,20 +872,24 @@ Run a network-connected Bitsocial node. Once the daemon is running you can creat

```
USAGE
$ bitsocial daemon --pkcRpcUrl <value> --logPath <value> [--chainProviderUrls <value>...]
[--allowPrivateKeyExport]
$ bitsocial daemon --pkcRpcUrl <value> --logPath <value> [--chainProviderUrls <value>...] [--enableIpfsGc]
[--ipfsGcIntervalMinutes <value>] [--allowPrivateKeyExport]

FLAGS
--[no-]allowPrivateKeyExport Allow RPC clients to request community exports that include the community signer's
private key (`bitsocial community export --includePrivateKey`). Disable with
--no-allowPrivateKeyExport when exposing the RPC to untrusted clients
--chainProviderUrls=<value>... [default:
https://eth.drpc.org,https://ethereum.publicnode.com,https://ethereum-rpc.publicnode.c
om,https://rpc.mevblocker.io,https://1rpc.io/eth,https://eth-pokt.nodies.app] RPC
URL(s) for .bso name resolution. Can be specified multiple times.
--logPath=<value> (required) [default: /home/runner/.local/state/bitsocial] Specify a directory which
will be used to store logs
--pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] Specify PKC RPC URL to listen on
--[no-]allowPrivateKeyExport Allow RPC clients to request community exports that include the community signer's
private key (`bitsocial community export --includePrivateKey`). Disable with
--no-allowPrivateKeyExport when exposing the RPC to untrusted clients
--chainProviderUrls=<value>... [default:
https://eth.drpc.org,https://ethereum.publicnode.com,https://ethereum-rpc.publicnode.
com,https://rpc.mevblocker.io,https://1rpc.io/eth,https://eth-pokt.nodies.app] RPC
URL(s) for .bso name resolution. Can be specified multiple times.
--[no-]enableIpfsGc Periodically garbage-collect the IPFS repo over the kubo RPC API while the daemon is
up. Only reclaims unpinned blocks — pinned data and MFS are never collected. Disable
with --no-enableIpfsGc
--ipfsGcIntervalMinutes=<value> [default: 60] How often to garbage-collect the IPFS repo, in minutes
--logPath=<value> (required) [default: /home/runner/.local/state/bitsocial] Specify a directory which
will be used to store logs
--pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] Specify PKC RPC URL to listen on

DESCRIPTION
Run a network-connected Bitsocial node. Once the daemon is running you can create and start your communities and
Expand Down
160 changes: 152 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@
"@oclif/plugin-help": "6.2.36",
"@oclif/plugin-not-found": "3.2.73",
"@oclif/table": "0.5.1",
"@pkcprotocol/pkc-js": "0.0.73",
"@pkcprotocol/pkc-js": "0.0.77",
"dataobject-parser": "1.2.22",
"decompress": "4.2.1",
"env-paths": "2.2.1",
"exit-hook": "4.0.0",
"express": "4.19.2",
"kubo": "0.42.0",
"kubo": "0.43.0",
"p-limit": "7.3.0",
"strip-json-comments": "5.0.3",
"tcp-port-used": "1.0.2",
Expand Down
48 changes: 40 additions & 8 deletions src/cli/commands/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChildProcessWithoutNullStreams } from "child_process";

import defaults from "../../common-utils/defaults.js";
import { startKuboNode } from "../../ipfs/startIpfs.js";
import { startRepoGcScheduler, DEFAULT_REPO_GC_INTERVAL_MS } from "../../ipfs/repoGc.js";
import path from "path";
import tcpPortUsed from "tcp-port-used";
import {
Expand Down Expand Up @@ -109,6 +110,19 @@ export default class Daemon extends Command {
default: DEFAULT_PROVIDERS
}),

enableIpfsGc: Flags.boolean({
description:
"Periodically garbage-collect the IPFS repo over the kubo RPC API while the daemon is up. Only reclaims unpinned blocks — pinned data and MFS are never collected. Disable with --no-enableIpfsGc",
allowNo: true,
default: true
}),

ipfsGcIntervalMinutes: Flags.integer({
description: "How often to garbage-collect the IPFS repo, in minutes",
default: DEFAULT_REPO_GC_INTERVAL_MS / 60_000,
min: 1
}),

allowPrivateKeyExport: Flags.boolean({
description:
"Allow RPC clients to request community exports that include the community signer's private key (`bitsocial community export --includePrivateKey`). Disable with --no-allowPrivateKeyExport when exposing the RPC to untrusted clients",
Expand Down Expand Up @@ -403,15 +417,20 @@ export default class Daemon extends Command {
);
}
let spawnedProcess: ChildProcessWithoutNullStreams | undefined;
const startPromise = startKuboNode(kuboRpcEndpoint, ipfsGatewayEndpoint, mergedPkcOptions.dataPath!, (process) => {
spawnedProcess = process;
kuboProcess = process;
if (process.pid) {
const pid = process.pid;
liveKuboPids.add(pid);
process.once("exit", () => liveKuboPids.delete(pid));
const startPromise = startKuboNode(
kuboRpcEndpoint,
ipfsGatewayEndpoint,
mergedPkcOptions.dataPath!,
(process) => {
spawnedProcess = process;
kuboProcess = process;
if (process.pid) {
const pid = process.pid;
liveKuboPids.add(pid);
process.once("exit", () => liveKuboPids.delete(pid));
}
}
});
);
pendingKuboStart = startPromise;
let startedProcess: ChildProcessWithoutNullStreams | undefined;
try {
Expand Down Expand Up @@ -537,6 +556,7 @@ export default class Daemon extends Command {
};

let keepKuboUpInterval: NodeJS.Timeout | undefined;
let stopRepoGcScheduler: (() => void) | undefined;
const { asyncExitHook } = await import("exit-hook");
const killKuboProcessGroup = (pid: number, signal: NodeJS.Signals) => {
// Kill the entire process group (negative PID) on non-Windows.
Expand Down Expand Up @@ -613,6 +633,7 @@ export default class Daemon extends Command {

const shutdownDaemon = async () => {
if (keepKuboUpInterval) clearInterval(keepKuboUpInterval);
stopRepoGcScheduler?.();
if (mainProcessExited) return; // we already exited
console.log(
"\nShutting down Bitsocial daemon, it may take a few seconds to shut down all communities and the IPFS node..."
Expand Down Expand Up @@ -715,6 +736,17 @@ export default class Daemon extends Command {
if (!pkcOptionsFromFlag?.kuboRpcClientsOptions) await keepKuboUp();
await createOrConnectRpc();

// Runs against whichever kubo the daemon ends up talking to, including one started by
// another program (--pkcOptions.kuboRpcClientsOptions). pkc-js also GCs, but only from
// a started local community's IPNS sync — a daemon that is up with no community
// started would otherwise never reclaim anything (issue #119).
if (flags.enableIpfsGc)
stopRepoGcScheduler = startRepoGcScheduler({
kuboApiUrl: kuboRpcEndpoint,
intervalMs: flags.ipfsGcIntervalMinutes * 60 * 1000,
log: PKCLogger("bitsocial-cli:ipfs:repoGc")
});

Comment on lines +739 to +749

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add daemon-level coverage for the GC flags and scheduler lifecycle.

test/kubo/repoGc.test.ts tests the scheduler directly. Add a daemon test for the default enabled state, --no-enableIpfsGc, minute-to-millisecond conversion, and shutdown calling the stop function.

As per coding guidelines, “Add a test when you add a feature or fix a bug.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/commands/daemon.ts` around lines 739 - 749, Add daemon-level tests
covering the enableIpfsGc default, disabling it with --no-enableIpfsGc,
conversion of ipfsGcIntervalMinutes to milliseconds, and invocation of the
scheduler’s stop function during shutdown. Exercise the daemon startup and
lifecycle path containing startRepoGcScheduler, while keeping direct scheduler
behavior covered by repoGc.test.ts.

Source: Coding guidelines

keepKuboUpInterval = setInterval(async () => {
if (mainProcessExited) return;
await runKeepKuboUpTick({
Expand Down
Loading