@bitsocial/bitsocial-cli 0.19.86 → 0.19.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -28
- package/dist/cli/commands/daemon.d.ts +2 -0
- package/dist/cli/commands/daemon.js +23 -0
- package/dist/ipfs/repoGc.d.ts +46 -0
- package/dist/ipfs/repoGc.js +101 -0
- package/oclif.manifest.json +15 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -421,7 +421,7 @@ EXAMPLES
|
|
|
421
421
|
$ bitsocial challenge install ./my-local-challenge
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
-
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
424
|
+
_See code: [src/cli/commands/challenge/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/challenge/install.ts)_
|
|
425
425
|
|
|
426
426
|
## `bitsocial challenge list`
|
|
427
427
|
|
|
@@ -447,7 +447,7 @@ EXAMPLES
|
|
|
447
447
|
$ bitsocial challenge list -q
|
|
448
448
|
```
|
|
449
449
|
|
|
450
|
-
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
450
|
+
_See code: [src/cli/commands/challenge/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/challenge/list.ts)_
|
|
451
451
|
|
|
452
452
|
## `bitsocial challenge ls`
|
|
453
453
|
|
|
@@ -501,7 +501,7 @@ EXAMPLES
|
|
|
501
501
|
$ bitsocial challenge remove @scope/my-challenge
|
|
502
502
|
```
|
|
503
503
|
|
|
504
|
-
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
504
|
+
_See code: [src/cli/commands/challenge/remove.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/challenge/remove.ts)_
|
|
505
505
|
|
|
506
506
|
## `bitsocial challenge rm NAME`
|
|
507
507
|
|
|
@@ -615,7 +615,7 @@ EXAMPLES
|
|
|
615
615
|
$ bitsocial community create --jsonFile ./create-options.json
|
|
616
616
|
```
|
|
617
617
|
|
|
618
|
-
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
618
|
+
_See code: [src/cli/commands/community/create.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/create.ts)_
|
|
619
619
|
|
|
620
620
|
## `bitsocial community delete ADDRESSES`
|
|
621
621
|
|
|
@@ -640,7 +640,7 @@ EXAMPLES
|
|
|
640
640
|
$ bitsocial community delete 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
641
641
|
```
|
|
642
642
|
|
|
643
|
-
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
643
|
+
_See code: [src/cli/commands/community/delete.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/delete.ts)_
|
|
644
644
|
|
|
645
645
|
## `bitsocial community edit ADDRESS`
|
|
646
646
|
|
|
@@ -710,7 +710,7 @@ EXAMPLES
|
|
|
710
710
|
$ bitsocial community edit bitsocial.bso --jsonFile ./edit-options.json
|
|
711
711
|
```
|
|
712
712
|
|
|
713
|
-
_See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
713
|
+
_See code: [src/cli/commands/community/edit.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/edit.ts)_
|
|
714
714
|
|
|
715
715
|
## `bitsocial community export [ADDRESS]`
|
|
716
716
|
|
|
@@ -751,7 +751,7 @@ EXAMPLES
|
|
|
751
751
|
$ bitsocial community export --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
752
752
|
```
|
|
753
753
|
|
|
754
|
-
_See code: [src/cli/commands/community/export.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
754
|
+
_See code: [src/cli/commands/community/export.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/export.ts)_
|
|
755
755
|
|
|
756
756
|
## `bitsocial community get [ADDRESS]`
|
|
757
757
|
|
|
@@ -782,7 +782,7 @@ EXAMPLES
|
|
|
782
782
|
$ bitsocial community get --publicKey 12D3KooWG3XbzoVyAE6Y9vHZKF64Yuuu4TjdgQKedk14iYmTEPWu
|
|
783
783
|
```
|
|
784
784
|
|
|
785
|
-
_See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
785
|
+
_See code: [src/cli/commands/community/get.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/get.ts)_
|
|
786
786
|
|
|
787
787
|
## `bitsocial community list`
|
|
788
788
|
|
|
@@ -805,7 +805,7 @@ EXAMPLES
|
|
|
805
805
|
$ bitsocial community list
|
|
806
806
|
```
|
|
807
807
|
|
|
808
|
-
_See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
808
|
+
_See code: [src/cli/commands/community/list.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/list.ts)_
|
|
809
809
|
|
|
810
810
|
## `bitsocial community start ADDRESSES`
|
|
811
811
|
|
|
@@ -839,7 +839,7 @@ EXAMPLES
|
|
|
839
839
|
$ bitsocial community start $(bitsocial community list -q) --concurrency 1
|
|
840
840
|
```
|
|
841
841
|
|
|
842
|
-
_See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
842
|
+
_See code: [src/cli/commands/community/start.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/start.ts)_
|
|
843
843
|
|
|
844
844
|
## `bitsocial community stop ADDRESSES`
|
|
845
845
|
|
|
@@ -864,7 +864,7 @@ EXAMPLES
|
|
|
864
864
|
$ bitsocial community stop Qmb99crTbSUfKXamXwZBe829Vf6w5w5TktPkb6WstC9RFW
|
|
865
865
|
```
|
|
866
866
|
|
|
867
|
-
_See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
867
|
+
_See code: [src/cli/commands/community/stop.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/community/stop.ts)_
|
|
868
868
|
|
|
869
869
|
## `bitsocial daemon`
|
|
870
870
|
|
|
@@ -872,20 +872,24 @@ Run a network-connected Bitsocial node. Once the daemon is running you can creat
|
|
|
872
872
|
|
|
873
873
|
```
|
|
874
874
|
USAGE
|
|
875
|
-
$ bitsocial daemon --pkcRpcUrl <value> --logPath <value> [--chainProviderUrls <value>...]
|
|
876
|
-
[--allowPrivateKeyExport]
|
|
875
|
+
$ bitsocial daemon --pkcRpcUrl <value> --logPath <value> [--chainProviderUrls <value>...] [--enableIpfsGc]
|
|
876
|
+
[--ipfsGcIntervalMinutes <value>] [--allowPrivateKeyExport]
|
|
877
877
|
|
|
878
878
|
FLAGS
|
|
879
|
-
--[no-]allowPrivateKeyExport
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
--chainProviderUrls=<value>...
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
--
|
|
887
|
-
|
|
888
|
-
|
|
879
|
+
--[no-]allowPrivateKeyExport Allow RPC clients to request community exports that include the community signer's
|
|
880
|
+
private key (`bitsocial community export --includePrivateKey`). Disable with
|
|
881
|
+
--no-allowPrivateKeyExport when exposing the RPC to untrusted clients
|
|
882
|
+
--chainProviderUrls=<value>... [default:
|
|
883
|
+
https://eth.drpc.org,https://ethereum.publicnode.com,https://ethereum-rpc.publicnode.
|
|
884
|
+
com,https://rpc.mevblocker.io,https://1rpc.io/eth,https://eth-pokt.nodies.app] RPC
|
|
885
|
+
URL(s) for .bso name resolution. Can be specified multiple times.
|
|
886
|
+
--[no-]enableIpfsGc Periodically garbage-collect the IPFS repo over the kubo RPC API while the daemon is
|
|
887
|
+
up. Only reclaims unpinned blocks — pinned data and MFS are never collected. Disable
|
|
888
|
+
with --no-enableIpfsGc
|
|
889
|
+
--ipfsGcIntervalMinutes=<value> [default: 60] How often to garbage-collect the IPFS repo, in minutes
|
|
890
|
+
--logPath=<value> (required) [default: /home/runner/.local/state/bitsocial] Specify a directory which
|
|
891
|
+
will be used to store logs
|
|
892
|
+
--pkcRpcUrl=<value> (required) [default: ws://localhost:9138/] Specify PKC RPC URL to listen on
|
|
889
893
|
|
|
890
894
|
DESCRIPTION
|
|
891
895
|
Run a network-connected Bitsocial node. Once the daemon is running you can create and start your communities and
|
|
@@ -911,7 +915,7 @@ EXAMPLES
|
|
|
911
915
|
$ bitsocial daemon --no-allowPrivateKeyExport
|
|
912
916
|
```
|
|
913
917
|
|
|
914
|
-
_See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
918
|
+
_See code: [src/cli/commands/daemon.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/daemon.ts)_
|
|
915
919
|
|
|
916
920
|
## `bitsocial help [COMMAND]`
|
|
917
921
|
|
|
@@ -977,7 +981,7 @@ EXAMPLES
|
|
|
977
981
|
$ bitsocial logs --stdout -f
|
|
978
982
|
```
|
|
979
983
|
|
|
980
|
-
_See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
984
|
+
_See code: [src/cli/commands/logs.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/logs.ts)_
|
|
981
985
|
|
|
982
986
|
## `bitsocial update check`
|
|
983
987
|
|
|
@@ -994,7 +998,7 @@ EXAMPLES
|
|
|
994
998
|
$ bitsocial update check
|
|
995
999
|
```
|
|
996
1000
|
|
|
997
|
-
_See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
1001
|
+
_See code: [src/cli/commands/update/check.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/update/check.ts)_
|
|
998
1002
|
|
|
999
1003
|
## `bitsocial update install [VERSION]`
|
|
1000
1004
|
|
|
@@ -1026,7 +1030,7 @@ EXAMPLES
|
|
|
1026
1030
|
$ bitsocial update install --no-restart-daemons
|
|
1027
1031
|
```
|
|
1028
1032
|
|
|
1029
|
-
_See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
1033
|
+
_See code: [src/cli/commands/update/install.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/update/install.ts)_
|
|
1030
1034
|
|
|
1031
1035
|
## `bitsocial update versions`
|
|
1032
1036
|
|
|
@@ -1048,7 +1052,7 @@ EXAMPLES
|
|
|
1048
1052
|
$ bitsocial update versions --limit 5
|
|
1049
1053
|
```
|
|
1050
1054
|
|
|
1051
|
-
_See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.
|
|
1055
|
+
_See code: [src/cli/commands/update/versions.ts](https://github.com/bitsocialnet/bitsocial-cli/blob/v0.19.87/src/cli/commands/update/versions.ts)_
|
|
1052
1056
|
<!-- commandsstop -->
|
|
1053
1057
|
|
|
1054
1058
|
## Contribution
|
|
@@ -26,6 +26,8 @@ export default class Daemon extends Command {
|
|
|
26
26
|
pkcRpcUrl: import("@oclif/core/interfaces").OptionFlag<import("url").URL, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
27
|
logPath: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
28
|
chainProviderUrls: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
29
|
+
enableIpfsGc: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
|
+
ipfsGcIntervalMinutes: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
31
|
allowPrivateKeyExport: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
32
|
};
|
|
31
33
|
static examples: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Flags, Command } from "@oclif/core";
|
|
2
2
|
import defaults from "../../common-utils/defaults.js";
|
|
3
3
|
import { startKuboNode } from "../../ipfs/startIpfs.js";
|
|
4
|
+
import { startRepoGcScheduler, DEFAULT_REPO_GC_INTERVAL_MS } from "../../ipfs/repoGc.js";
|
|
4
5
|
import path from "path";
|
|
5
6
|
import tcpPortUsed from "tcp-port-used";
|
|
6
7
|
import { getLanIpV4Address, PKCLogger, setupDebugLogger, loadKuboConfigFile, parseMultiAddrKuboRpcToUrl, parseMultiAddrIpfsGatewayToUrl } from "../../util.js";
|
|
@@ -84,6 +85,16 @@ export default class Daemon extends Command {
|
|
|
84
85
|
multiple: true,
|
|
85
86
|
default: DEFAULT_PROVIDERS
|
|
86
87
|
}),
|
|
88
|
+
enableIpfsGc: Flags.boolean({
|
|
89
|
+
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",
|
|
90
|
+
allowNo: true,
|
|
91
|
+
default: true
|
|
92
|
+
}),
|
|
93
|
+
ipfsGcIntervalMinutes: Flags.integer({
|
|
94
|
+
description: "How often to garbage-collect the IPFS repo, in minutes",
|
|
95
|
+
default: DEFAULT_REPO_GC_INTERVAL_MS / 60_000,
|
|
96
|
+
min: 1
|
|
97
|
+
}),
|
|
87
98
|
allowPrivateKeyExport: Flags.boolean({
|
|
88
99
|
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",
|
|
89
100
|
allowNo: true,
|
|
@@ -465,6 +476,7 @@ export default class Daemon extends Command {
|
|
|
465
476
|
}
|
|
466
477
|
};
|
|
467
478
|
let keepKuboUpInterval;
|
|
479
|
+
let stopRepoGcScheduler;
|
|
468
480
|
const { asyncExitHook } = await import("exit-hook");
|
|
469
481
|
const killKuboProcessGroup = (pid, signal) => {
|
|
470
482
|
// Kill the entire process group (negative PID) on non-Windows.
|
|
@@ -546,6 +558,7 @@ export default class Daemon extends Command {
|
|
|
546
558
|
const shutdownDaemon = async () => {
|
|
547
559
|
if (keepKuboUpInterval)
|
|
548
560
|
clearInterval(keepKuboUpInterval);
|
|
561
|
+
stopRepoGcScheduler?.();
|
|
549
562
|
if (mainProcessExited)
|
|
550
563
|
return; // we already exited
|
|
551
564
|
console.log("\nShutting down Bitsocial daemon, it may take a few seconds to shut down all communities and the IPFS node...");
|
|
@@ -637,6 +650,16 @@ export default class Daemon extends Command {
|
|
|
637
650
|
if (!pkcOptionsFromFlag?.kuboRpcClientsOptions)
|
|
638
651
|
await keepKuboUp();
|
|
639
652
|
await createOrConnectRpc();
|
|
653
|
+
// Runs against whichever kubo the daemon ends up talking to, including one started by
|
|
654
|
+
// another program (--pkcOptions.kuboRpcClientsOptions). pkc-js also GCs, but only from
|
|
655
|
+
// a started local community's IPNS sync — a daemon that is up with no community
|
|
656
|
+
// started would otherwise never reclaim anything (issue #119).
|
|
657
|
+
if (flags.enableIpfsGc)
|
|
658
|
+
stopRepoGcScheduler = startRepoGcScheduler({
|
|
659
|
+
kuboApiUrl: kuboRpcEndpoint,
|
|
660
|
+
intervalMs: flags.ipfsGcIntervalMinutes * 60 * 1000,
|
|
661
|
+
log: PKCLogger("bitsocial-cli:ipfs:repoGc")
|
|
662
|
+
});
|
|
640
663
|
keepKuboUpInterval = setInterval(async () => {
|
|
641
664
|
if (mainProcessExited)
|
|
642
665
|
return;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const DEFAULT_REPO_GC_INTERVAL_MS: number;
|
|
2
|
+
type FetchLike = (input: string, init?: {
|
|
3
|
+
method?: string;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}) => Promise<Response>;
|
|
6
|
+
/**
|
|
7
|
+
* Runs `repo gc` over the kubo RPC API. GC only ever reclaims unpinned blocks, so this is safe to
|
|
8
|
+
* run unconditionally — kubo decides what is actually collectable.
|
|
9
|
+
*
|
|
10
|
+
* We drive GC over RPC rather than passing kubo's own `--enable-gc` daemon flag. A kubo daemon
|
|
11
|
+
* started with `--enable-gc` never exits in response to `POST /api/v0/shutdown` — it logs
|
|
12
|
+
* "cannot access config, repo not open" and lingers indefinitely as a half-shutdown zombie
|
|
13
|
+
* (SIGTERM still works; verified permanent past 300s). pkc-js POSTs that exact endpoint when it
|
|
14
|
+
* rewrites the kubo Routing config on first connect and relies on the daemon restarting kubo
|
|
15
|
+
* afterwards, so `--enable-gc` wedges the supervision loop.
|
|
16
|
+
*
|
|
17
|
+
* Reported upstream as ipfs/kubo#11424: `maybeRunGC` gives `PeriodicGC` the command request
|
|
18
|
+
* context, but the shutdown command only calls `nd.Close()`, which cancels the node context — so
|
|
19
|
+
* `gcErrc` never closes and `daemonFunc` blocks forever draining it. Reproduces on 0.24.0, 0.42.0
|
|
20
|
+
* and 0.43.0, which is also why the earlier attempt at the flag (f228d7d, Dec 2023, kubo ~0.24)
|
|
21
|
+
* was reverted two days later. Do not add `--enable-gc` back without re-testing that path.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runRepoGc(options: {
|
|
24
|
+
kuboApiUrl: URL | string;
|
|
25
|
+
log?: any;
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
fetchImpl?: FetchLike;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
ran: boolean;
|
|
30
|
+
reclaimedCids?: number;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Starts the periodic repo GC. Returns a stop function.
|
|
34
|
+
*
|
|
35
|
+
* The timer is unref'd so it never by itself keeps the daemon process alive, and runs are
|
|
36
|
+
* single-flighted: a GC that outlives its own interval must not have a second one stacked on top
|
|
37
|
+
* of it. Errors are swallowed inside the tick — an interval callback that rejects becomes an
|
|
38
|
+
* unhandledRejection and takes the daemon down (same failure mode as issue #37 bug 3).
|
|
39
|
+
*/
|
|
40
|
+
export declare function startRepoGcScheduler(options: {
|
|
41
|
+
kuboApiUrl: URL | string;
|
|
42
|
+
intervalMs?: number;
|
|
43
|
+
log?: any;
|
|
44
|
+
fetchImpl?: FetchLike;
|
|
45
|
+
}): () => void;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { PKCLogger } from "../util.js";
|
|
2
|
+
// Matches kubo's default Datastore.GCPeriod.
|
|
3
|
+
export const DEFAULT_REPO_GC_INTERVAL_MS = 60 * 60 * 1000;
|
|
4
|
+
// The daemon tracks kubo at its configured bind address, which is routinely a wildcard
|
|
5
|
+
// (`http://0.0.0.0:5001/api/v0`). Connecting to 0.0.0.0 fails with EINVAL on macOS, so resolve
|
|
6
|
+
// wildcards to loopback the same way daemon.ts does for its port checks.
|
|
7
|
+
function toConnectableApiBase(kuboApiUrl) {
|
|
8
|
+
const url = new URL(String(kuboApiUrl));
|
|
9
|
+
if (url.hostname === "0.0.0.0")
|
|
10
|
+
url.hostname = "127.0.0.1";
|
|
11
|
+
else if (url.hostname === "::" || url.hostname === "[::]")
|
|
12
|
+
url.hostname = "[::1]";
|
|
13
|
+
return url.toString().replace(/\/+$/, "");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs `repo gc` over the kubo RPC API. GC only ever reclaims unpinned blocks, so this is safe to
|
|
17
|
+
* run unconditionally — kubo decides what is actually collectable.
|
|
18
|
+
*
|
|
19
|
+
* We drive GC over RPC rather than passing kubo's own `--enable-gc` daemon flag. A kubo daemon
|
|
20
|
+
* started with `--enable-gc` never exits in response to `POST /api/v0/shutdown` — it logs
|
|
21
|
+
* "cannot access config, repo not open" and lingers indefinitely as a half-shutdown zombie
|
|
22
|
+
* (SIGTERM still works; verified permanent past 300s). pkc-js POSTs that exact endpoint when it
|
|
23
|
+
* rewrites the kubo Routing config on first connect and relies on the daemon restarting kubo
|
|
24
|
+
* afterwards, so `--enable-gc` wedges the supervision loop.
|
|
25
|
+
*
|
|
26
|
+
* Reported upstream as ipfs/kubo#11424: `maybeRunGC` gives `PeriodicGC` the command request
|
|
27
|
+
* context, but the shutdown command only calls `nd.Close()`, which cancels the node context — so
|
|
28
|
+
* `gcErrc` never closes and `daemonFunc` blocks forever draining it. Reproduces on 0.24.0, 0.42.0
|
|
29
|
+
* and 0.43.0, which is also why the earlier attempt at the flag (f228d7d, Dec 2023, kubo ~0.24)
|
|
30
|
+
* was reverted two days later. Do not add `--enable-gc` back without re-testing that path.
|
|
31
|
+
*/
|
|
32
|
+
export async function runRepoGc(options) {
|
|
33
|
+
const log = options.log ?? PKCLogger("bitsocial-cli:ipfs:repoGc");
|
|
34
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
35
|
+
const apiBase = toConnectableApiBase(options.kuboApiUrl);
|
|
36
|
+
let reclaimedCids = 0;
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetchImpl(`${apiBase}/repo/gc?quiet=true`, { method: "POST", signal: options.signal });
|
|
39
|
+
if (!response.ok)
|
|
40
|
+
throw new Error(`kubo RPC repo/gc responded ${response.status} ${response.statusText}`);
|
|
41
|
+
// repo/gc streams newline-delimited JSON, one object per reclaimed CID. Draining it fully
|
|
42
|
+
// is what makes this await mean "GC finished" rather than "GC started".
|
|
43
|
+
const text = await response.text();
|
|
44
|
+
for (const line of text.split("\n")) {
|
|
45
|
+
const trimmed = line.trim();
|
|
46
|
+
if (!trimmed)
|
|
47
|
+
continue;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(trimmed);
|
|
50
|
+
if (parsed.Error)
|
|
51
|
+
log.error?.("Failed to GC a block out of the ipfs repo", parsed.Error);
|
|
52
|
+
else if (parsed.Key)
|
|
53
|
+
reclaimedCids++;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// A malformed line is not worth aborting a completed GC over.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
log.error?.("Failed to GC ipfs repo", apiBase, error);
|
|
62
|
+
return { ran: false };
|
|
63
|
+
}
|
|
64
|
+
log(`GC reclaimed ${reclaimedCids} cids from the IPFS node ${apiBase}`);
|
|
65
|
+
return { ran: true, reclaimedCids };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Starts the periodic repo GC. Returns a stop function.
|
|
69
|
+
*
|
|
70
|
+
* The timer is unref'd so it never by itself keeps the daemon process alive, and runs are
|
|
71
|
+
* single-flighted: a GC that outlives its own interval must not have a second one stacked on top
|
|
72
|
+
* of it. Errors are swallowed inside the tick — an interval callback that rejects becomes an
|
|
73
|
+
* unhandledRejection and takes the daemon down (same failure mode as issue #37 bug 3).
|
|
74
|
+
*/
|
|
75
|
+
export function startRepoGcScheduler(options) {
|
|
76
|
+
const log = options.log ?? PKCLogger("bitsocial-cli:ipfs:repoGc");
|
|
77
|
+
const intervalMs = options.intervalMs ?? DEFAULT_REPO_GC_INTERVAL_MS;
|
|
78
|
+
const abortController = new AbortController();
|
|
79
|
+
let inFlight;
|
|
80
|
+
const tick = () => {
|
|
81
|
+
if (inFlight)
|
|
82
|
+
return;
|
|
83
|
+
inFlight = runRepoGc({
|
|
84
|
+
kuboApiUrl: options.kuboApiUrl,
|
|
85
|
+
log,
|
|
86
|
+
signal: abortController.signal,
|
|
87
|
+
fetchImpl: options.fetchImpl
|
|
88
|
+
})
|
|
89
|
+
.catch((error) => log.error?.("repo gc tick error (will retry next interval)", error))
|
|
90
|
+
.finally(() => {
|
|
91
|
+
inFlight = undefined;
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const timer = setInterval(tick, intervalMs);
|
|
95
|
+
timer.unref?.();
|
|
96
|
+
log(`Scheduled IPFS repo GC every ${intervalMs}ms against ${String(options.kuboApiUrl)}`);
|
|
97
|
+
return () => {
|
|
98
|
+
clearInterval(timer);
|
|
99
|
+
abortController.abort();
|
|
100
|
+
};
|
|
101
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -46,6 +46,20 @@
|
|
|
46
46
|
"multiple": true,
|
|
47
47
|
"type": "option"
|
|
48
48
|
},
|
|
49
|
+
"enableIpfsGc": {
|
|
50
|
+
"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",
|
|
51
|
+
"name": "enableIpfsGc",
|
|
52
|
+
"allowNo": true,
|
|
53
|
+
"type": "boolean"
|
|
54
|
+
},
|
|
55
|
+
"ipfsGcIntervalMinutes": {
|
|
56
|
+
"description": "How often to garbage-collect the IPFS repo, in minutes",
|
|
57
|
+
"name": "ipfsGcIntervalMinutes",
|
|
58
|
+
"default": 60,
|
|
59
|
+
"hasDynamicHelp": false,
|
|
60
|
+
"multiple": false,
|
|
61
|
+
"type": "option"
|
|
62
|
+
},
|
|
49
63
|
"allowPrivateKeyExport": {
|
|
50
64
|
"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",
|
|
51
65
|
"name": "allowPrivateKeyExport",
|
|
@@ -871,5 +885,5 @@
|
|
|
871
885
|
]
|
|
872
886
|
}
|
|
873
887
|
},
|
|
874
|
-
"version": "0.19.
|
|
888
|
+
"version": "0.19.87"
|
|
875
889
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitsocial/bitsocial-cli",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.87",
|
|
4
4
|
"description": "Command line interface to Bitsocial API",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"homepage": "https://github.com/bitsocialnet/bitsocial-cli",
|
|
@@ -119,13 +119,13 @@
|
|
|
119
119
|
"@oclif/plugin-help": "6.2.36",
|
|
120
120
|
"@oclif/plugin-not-found": "3.2.73",
|
|
121
121
|
"@oclif/table": "0.5.1",
|
|
122
|
-
"@pkcprotocol/pkc-js": "0.0.
|
|
122
|
+
"@pkcprotocol/pkc-js": "0.0.77",
|
|
123
123
|
"dataobject-parser": "1.2.22",
|
|
124
124
|
"decompress": "4.2.1",
|
|
125
125
|
"env-paths": "2.2.1",
|
|
126
126
|
"exit-hook": "4.0.0",
|
|
127
127
|
"express": "4.19.2",
|
|
128
|
-
"kubo": "0.
|
|
128
|
+
"kubo": "0.43.0",
|
|
129
129
|
"p-limit": "7.3.0",
|
|
130
130
|
"strip-json-comments": "5.0.3",
|
|
131
131
|
"tcp-port-used": "1.0.2",
|