pinnace 0.2.0 → 0.3.1
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 +5 -3
- package/dist/cli/bin.js +8 -2
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/startup.d.ts +19 -0
- package/dist/cli/startup.d.ts.map +1 -0
- package/dist/cli/startup.js +46 -0
- package/dist/cli/startup.js.map +1 -0
- package/dist/provision/cloud-init.d.ts.map +1 -1
- package/dist/provision/cloud-init.js +11 -2
- package/dist/provision/cloud-init.js.map +1 -1
- package/package.json +3 -2
- package/src/cli/bin.ts +8 -2
- package/src/cli/startup.ts +59 -0
- package/src/provision/cloud-init.ts +11 -2
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@ Requires Node >= 22.
|
|
|
26
26
|
|
|
27
27
|
## Configuration + secrets
|
|
28
28
|
|
|
29
|
-
Every setting resolves **CLI arg > env
|
|
29
|
+
Every setting resolves **CLI arg > exported env > `.env.local` > `.env` > `pinnace.json`**.
|
|
30
|
+
|
|
31
|
+
On startup the `pinnace` bin auto-loads `.env` then `.env.local` from the current directory into the environment (via `ldenv`), so a global install (`npm install -g pinnace`) picks up your secrets with no wrapper: just drop them in `.env.local` and run `pinnace …`. Loading is silent and cwd-only (never a home/global location), and it only AUGMENTS the environment: a value you exported explicitly still wins over the file (`.env.local` overrides `.env`, both sit below an exported var and above `pinnace.json`).
|
|
30
32
|
|
|
31
33
|
`pinnace.json` holds only NON-secret structure (commit-safe):
|
|
32
34
|
|
|
@@ -46,13 +48,13 @@ Every setting resolves **CLI arg > env (via `ldenv`) > `pinnace.json`**.
|
|
|
46
48
|
Secrets are **env-only, never in the config file** (structurally: the resolver has no file path for them). Each host's bearer token is read from `PINNACE_HOST_<NAME>_TOKEN` (the name upper-cased), and the master from `PINNACE_MASTER`:
|
|
47
49
|
|
|
48
50
|
```sh
|
|
49
|
-
# .env.local (git-ignored) — the ONLY place secrets live
|
|
51
|
+
# .env.local (git-ignored) — the ONLY place secrets live; auto-loaded from cwd
|
|
50
52
|
PINNACE_MASTER=<your high-entropy master secret>
|
|
51
53
|
PINNACE_HOST_PUBLISHER_TOKEN=<publisher bearer token>
|
|
52
54
|
PINNACE_HOST_REPLICA_TOKEN=<replica bearer token>
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
A missing token fails loud (naming the exact env var), never a silent empty token. Point the CLI at a config anywhere with `--config <path>`.
|
|
57
|
+
This `.env.local` is loaded automatically from the directory you run `pinnace` in (no `export` needed); an explicitly exported value still takes precedence over it. A missing token fails loud (naming the exact env var), never a silent empty token. Point the CLI at a config anywhere with `--config <path>`.
|
|
56
58
|
|
|
57
59
|
## The end-to-end setup
|
|
58
60
|
|
package/dist/cli/bin.js
CHANGED
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
* on-box `pinnace node <verb>` subcommands share this same binary but are wired
|
|
9
9
|
* by their own task. Config/env/core are all injectable there so the dispatch
|
|
10
10
|
* is unit-tested without a process or the operator's real env/config.
|
|
11
|
+
*
|
|
12
|
+
* Startup: the bin calls the {@link main} shim (`./startup.ts`), which loads the
|
|
13
|
+
* cwd's `.env` / `.env.local` into `process.env` via `ldenv` BEFORE `run()`
|
|
14
|
+
* reads env — so a global `npm install -g pinnace` gets the env-only secrets
|
|
15
|
+
* model with no `pnpm ldenv` dev wrapper. The pure `run(argv, {env})` path stays
|
|
16
|
+
* hermetic (the dotenv load lives in the shim, not in `run()`).
|
|
11
17
|
*/
|
|
12
|
-
import {
|
|
13
|
-
|
|
18
|
+
import { main } from './startup.js';
|
|
19
|
+
main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
|
|
14
20
|
console.error(err instanceof Error ? err.message : String(err));
|
|
15
21
|
process.exit(1);
|
|
16
22
|
});
|
package/dist/cli/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAC,IAAI,EAAC,MAAM,cAAc,CAAC;AAElC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC/B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,GAAG,EAAE,EAAE;IACP,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CACD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Injectable seams for the startup shim (defaulted to the real ones). */
|
|
2
|
+
export interface StartupOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Load `.env` / `.env.local` from the cwd into `process.env`. Defaults to
|
|
5
|
+
* `ldenv`'s `loadEnv` (reads `.env` then `.env.local`, local overriding,
|
|
6
|
+
* never overriding an already-exported value). Injected only for tests.
|
|
7
|
+
*/
|
|
8
|
+
loadDotEnv?: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The CLI startup entry: load the cwd's `.env` / `.env.local` into
|
|
12
|
+
* `process.env` (so a global install gets the env-only secrets), THEN dispatch
|
|
13
|
+
* `run()` over the process argv. Returns the process exit code.
|
|
14
|
+
*
|
|
15
|
+
* This is what the `pinnace` bin calls. It keeps the `loadEnv()` side effect
|
|
16
|
+
* out of the pure `run()` path so `run(argv, {env})` stays hermetic.
|
|
17
|
+
*/
|
|
18
|
+
export declare function main(argv?: readonly string[], options?: StartupOptions): Promise<number>;
|
|
19
|
+
//# sourceMappingURL=startup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../../src/cli/startup.ts"],"names":[],"mappings":"AA+BA,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,CACzB,IAAI,GAAE,SAAS,MAAM,EAA0B,EAC/C,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAMjB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `pinnace` CLI STARTUP shim: the thin process-entry glue that runs BEFORE
|
|
3
|
+
* the pure {@link run} dispatch. Its whole job is to make the env-only secrets
|
|
4
|
+
* model (CONTEXT.md `master key` / `token`) ergonomic for EVERY user, including
|
|
5
|
+
* a global `npm install -g pinnace`, by loading `.env` / `.env.local` from the
|
|
6
|
+
* cwd into `process.env` at startup, so no `pnpm ldenv` dev wrapper is needed.
|
|
7
|
+
*
|
|
8
|
+
* WHY a shim (and not inside {@link run}): `run(argv, {env})` is pure and
|
|
9
|
+
* hermetically unit-tested with an INJECTED `env` record; it must never read a
|
|
10
|
+
* real `.env` / touch a real cwd. So the ONE impure, filesystem-touching step
|
|
11
|
+
* (`ldenv`'s {@link loadEnv}) lives HERE, in the bin/startup layer, in front of
|
|
12
|
+
* `run()`. Config resolution + {@link resolveMasterSecret} read `process.env`,
|
|
13
|
+
* so `loadEnv()` must have mutated it BEFORE `run()` runs.
|
|
14
|
+
*
|
|
15
|
+
* PRECEDENCE (preserved, not changed). `loadEnv()` reads `.env` then
|
|
16
|
+
* `.env.local` (local overriding default) and MUTATES `process.env`, but it
|
|
17
|
+
* does NOT override a value ALREADY set in `process.env`. So dotenv slots in
|
|
18
|
+
* BELOW an explicitly-exported env var and ABOVE `pinnace.json`, keeping the
|
|
19
|
+
* documented "env > file" rule intact. The effective chain becomes:
|
|
20
|
+
* **CLI arg > exported `process.env` > `.env.local` > `.env` > `pinnace.json`**.
|
|
21
|
+
* The master + host tokens stay env-only: `.env.local` is merely a FILE SOURCE
|
|
22
|
+
* for those env vars, never a `pinnace.json` field.
|
|
23
|
+
*
|
|
24
|
+
* The `loadEnv` call is injected (defaulting to the real `ldenv` one) purely so
|
|
25
|
+
* the shim is unit-testable; production always uses the real `ldenv.loadEnv`,
|
|
26
|
+
* which is cwd-based only (never a global/home location), silent, and only
|
|
27
|
+
* AUGMENTS env — an already-exported value is left untouched.
|
|
28
|
+
*/
|
|
29
|
+
import { loadEnv } from 'ldenv';
|
|
30
|
+
import { run } from './run.js';
|
|
31
|
+
/**
|
|
32
|
+
* The CLI startup entry: load the cwd's `.env` / `.env.local` into
|
|
33
|
+
* `process.env` (so a global install gets the env-only secrets), THEN dispatch
|
|
34
|
+
* `run()` over the process argv. Returns the process exit code.
|
|
35
|
+
*
|
|
36
|
+
* This is what the `pinnace` bin calls. It keeps the `loadEnv()` side effect
|
|
37
|
+
* out of the pure `run()` path so `run(argv, {env})` stays hermetic.
|
|
38
|
+
*/
|
|
39
|
+
export async function main(argv = process.argv.slice(2), options = {}) {
|
|
40
|
+
const loadDotEnv = options.loadDotEnv ?? (() => void loadEnv());
|
|
41
|
+
// Load the cwd's dotenv files BEFORE run() reads process.env. This only
|
|
42
|
+
// augments process.env; an already-exported value still wins.
|
|
43
|
+
loadDotEnv();
|
|
44
|
+
return run(argv);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../../src/cli/startup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAC,GAAG,EAAC,MAAM,UAAU,CAAC;AAY7B;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACzB,OAA0B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAC/C,UAA0B,EAAE;IAE5B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;IAChE,wEAAwE;IACxE,8DAA8D;IAC9D,UAAU,EAAE,CAAC;IACb,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-init.d.ts","sourceRoot":"","sources":["../../src/provision/cloud-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gCAAgC,CAAC;AAE7D,8DAA8D;AAC9D,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC;AAEjC,yEAAyE;AACzE,eAAO,MAAM,cAAc,EAAE,SAAS,QAAQ,EAAgB,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,mDAAmD;IACnD,IAAI,EAAE,QAAQ,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC3B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC/B,mEAAmE;IACnE,IAAI,EAAE,QAAQ,CAAC;IACf,6CAA6C;IAC7C,SAAS,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,0EAA0E;IAC1E,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAAC;CAClD;
|
|
1
|
+
{"version":3,"file":"cloud-init.d.ts","sourceRoot":"","sources":["../../src/provision/cloud-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gCAAgC,CAAC;AAE7D,8DAA8D;AAC9D,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC;AAEjC,yEAAyE;AACzE,eAAO,MAAM,cAAc,EAAE,SAAS,QAAQ,EAAgB,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,mDAAmD;IACnD,IAAI,EAAE,QAAQ,CAAC;IACf,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC3B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC/B,mEAAmE;IACnE,IAAI,EAAE,QAAQ,CAAC;IACf,6CAA6C;IAC7C,SAAS,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,0EAA0E;IAC1E,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAAC;CAClD;AA8cD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,YAWjC,CAAC;AAOF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAUhE"}
|
|
@@ -10,7 +10,7 @@ const DEFAULT_KUBO_VERSION = 'v0.38.1';
|
|
|
10
10
|
* the same agent. Overridable per-box via {@link ProvisionInput.pinnaceVersion}.
|
|
11
11
|
* `pinnace@0.1.0` is the first published release (npm, public, OIDC provenance).
|
|
12
12
|
*/
|
|
13
|
-
const DEFAULT_PINNACE_VERSION = '0.
|
|
13
|
+
const DEFAULT_PINNACE_VERSION = '0.3.1';
|
|
14
14
|
/**
|
|
15
15
|
* The pinned Node.js major the box installs via NodeSource (`setup_<this>.x`).
|
|
16
16
|
* Node 22 is a current active LTS; Node 20 (the old literal) is the OLDEST LTS
|
|
@@ -252,7 +252,10 @@ write_files:
|
|
|
252
252
|
cfg Routing.Type "auto"
|
|
253
253
|
# Re-announce everything we serve; interval well under the record expiry.
|
|
254
254
|
# (Kubo 0.38 uses Provide.*; the pre-0.38 keys FATAL the daemon at boot.)
|
|
255
|
-
|
|
255
|
+
# Provide.Interval is a DURATION value Kubo requires as JSON. Without --json,
|
|
256
|
+
# setting it errors (not found / maybe use --json) and, under set -e, aborts
|
|
257
|
+
# this whole setup script (leaving the box half-provisioned).
|
|
258
|
+
cfg --json Provide.Interval '"12h"'
|
|
256
259
|
cfg Provide.Strategy "all"
|
|
257
260
|
|
|
258
261
|
# --- Resource hygiene for a small box ---
|
|
@@ -288,6 +291,12 @@ write_files:
|
|
|
288
291
|
User=ipfs
|
|
289
292
|
Group=ipfs
|
|
290
293
|
Environment=IPFS_PATH=/var/lib/ipfs/.ipfs
|
|
294
|
+
# Guarantee the datadir exists (owned by ipfs) BEFORE the sandboxed daemon
|
|
295
|
+
# starts: ReadWritePaths=/var/lib/ipfs requires the path to exist at unit
|
|
296
|
+
# start, so a missing datadir otherwise fails namespace setup with
|
|
297
|
+
# 226/NAMESPACE and crash-loops. The leading '+' runs this as root, outside
|
|
298
|
+
# the sandbox; it is idempotent.
|
|
299
|
+
ExecStartPre=+/usr/bin/install -d -o ipfs -g ipfs /var/lib/ipfs
|
|
291
300
|
ExecStart=/usr/local/bin/ipfs daemon --migrate=true --enable-gc
|
|
292
301
|
Restart=on-failure
|
|
293
302
|
RestartSec=5
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-init.js","sourceRoot":"","sources":["../../src/provision/cloud-init.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cloud-init.js","sourceRoot":"","sources":["../../src/provision/cloud-init.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinnace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"description": "Self-host a static website on IPFS across one or more self-owned Kubo nodes.",
|
|
6
6
|
"repository": {
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"files-from-path": "^1.1.4",
|
|
30
|
-
"ipfs-car": "^3.1.0"
|
|
30
|
+
"ipfs-car": "^3.1.0",
|
|
31
|
+
"ldenv": "^0.3.16"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@ipld/car": "^5.4.6",
|
package/src/cli/bin.ts
CHANGED
|
@@ -8,10 +8,16 @@
|
|
|
8
8
|
* on-box `pinnace node <verb>` subcommands share this same binary but are wired
|
|
9
9
|
* by their own task. Config/env/core are all injectable there so the dispatch
|
|
10
10
|
* is unit-tested without a process or the operator's real env/config.
|
|
11
|
+
*
|
|
12
|
+
* Startup: the bin calls the {@link main} shim (`./startup.ts`), which loads the
|
|
13
|
+
* cwd's `.env` / `.env.local` into `process.env` via `ldenv` BEFORE `run()`
|
|
14
|
+
* reads env — so a global `npm install -g pinnace` gets the env-only secrets
|
|
15
|
+
* model with no `pnpm ldenv` dev wrapper. The pure `run(argv, {env})` path stays
|
|
16
|
+
* hermetic (the dotenv load lives in the shim, not in `run()`).
|
|
11
17
|
*/
|
|
12
|
-
import {
|
|
18
|
+
import {main} from './startup.js';
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
main(process.argv.slice(2)).then(
|
|
15
21
|
(code) => process.exit(code),
|
|
16
22
|
(err) => {
|
|
17
23
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `pinnace` CLI STARTUP shim: the thin process-entry glue that runs BEFORE
|
|
3
|
+
* the pure {@link run} dispatch. Its whole job is to make the env-only secrets
|
|
4
|
+
* model (CONTEXT.md `master key` / `token`) ergonomic for EVERY user, including
|
|
5
|
+
* a global `npm install -g pinnace`, by loading `.env` / `.env.local` from the
|
|
6
|
+
* cwd into `process.env` at startup, so no `pnpm ldenv` dev wrapper is needed.
|
|
7
|
+
*
|
|
8
|
+
* WHY a shim (and not inside {@link run}): `run(argv, {env})` is pure and
|
|
9
|
+
* hermetically unit-tested with an INJECTED `env` record; it must never read a
|
|
10
|
+
* real `.env` / touch a real cwd. So the ONE impure, filesystem-touching step
|
|
11
|
+
* (`ldenv`'s {@link loadEnv}) lives HERE, in the bin/startup layer, in front of
|
|
12
|
+
* `run()`. Config resolution + {@link resolveMasterSecret} read `process.env`,
|
|
13
|
+
* so `loadEnv()` must have mutated it BEFORE `run()` runs.
|
|
14
|
+
*
|
|
15
|
+
* PRECEDENCE (preserved, not changed). `loadEnv()` reads `.env` then
|
|
16
|
+
* `.env.local` (local overriding default) and MUTATES `process.env`, but it
|
|
17
|
+
* does NOT override a value ALREADY set in `process.env`. So dotenv slots in
|
|
18
|
+
* BELOW an explicitly-exported env var and ABOVE `pinnace.json`, keeping the
|
|
19
|
+
* documented "env > file" rule intact. The effective chain becomes:
|
|
20
|
+
* **CLI arg > exported `process.env` > `.env.local` > `.env` > `pinnace.json`**.
|
|
21
|
+
* The master + host tokens stay env-only: `.env.local` is merely a FILE SOURCE
|
|
22
|
+
* for those env vars, never a `pinnace.json` field.
|
|
23
|
+
*
|
|
24
|
+
* The `loadEnv` call is injected (defaulting to the real `ldenv` one) purely so
|
|
25
|
+
* the shim is unit-testable; production always uses the real `ldenv.loadEnv`,
|
|
26
|
+
* which is cwd-based only (never a global/home location), silent, and only
|
|
27
|
+
* AUGMENTS env — an already-exported value is left untouched.
|
|
28
|
+
*/
|
|
29
|
+
import {loadEnv} from 'ldenv';
|
|
30
|
+
import {run} from './run.js';
|
|
31
|
+
|
|
32
|
+
/** Injectable seams for the startup shim (defaulted to the real ones). */
|
|
33
|
+
export interface StartupOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Load `.env` / `.env.local` from the cwd into `process.env`. Defaults to
|
|
36
|
+
* `ldenv`'s `loadEnv` (reads `.env` then `.env.local`, local overriding,
|
|
37
|
+
* never overriding an already-exported value). Injected only for tests.
|
|
38
|
+
*/
|
|
39
|
+
loadDotEnv?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The CLI startup entry: load the cwd's `.env` / `.env.local` into
|
|
44
|
+
* `process.env` (so a global install gets the env-only secrets), THEN dispatch
|
|
45
|
+
* `run()` over the process argv. Returns the process exit code.
|
|
46
|
+
*
|
|
47
|
+
* This is what the `pinnace` bin calls. It keeps the `loadEnv()` side effect
|
|
48
|
+
* out of the pure `run()` path so `run(argv, {env})` stays hermetic.
|
|
49
|
+
*/
|
|
50
|
+
export async function main(
|
|
51
|
+
argv: readonly string[] = process.argv.slice(2),
|
|
52
|
+
options: StartupOptions = {},
|
|
53
|
+
): Promise<number> {
|
|
54
|
+
const loadDotEnv = options.loadDotEnv ?? (() => void loadEnv());
|
|
55
|
+
// Load the cwd's dotenv files BEFORE run() reads process.env. This only
|
|
56
|
+
// augments process.env; an already-exported value still wins.
|
|
57
|
+
loadDotEnv();
|
|
58
|
+
return run(argv);
|
|
59
|
+
}
|
|
@@ -164,7 +164,7 @@ const DEFAULT_KUBO_VERSION = 'v0.38.1';
|
|
|
164
164
|
* the same agent. Overridable per-box via {@link ProvisionInput.pinnaceVersion}.
|
|
165
165
|
* `pinnace@0.1.0` is the first published release (npm, public, OIDC provenance).
|
|
166
166
|
*/
|
|
167
|
-
const DEFAULT_PINNACE_VERSION = '0.
|
|
167
|
+
const DEFAULT_PINNACE_VERSION = '0.3.1';
|
|
168
168
|
/**
|
|
169
169
|
* The pinned Node.js major the box installs via NodeSource (`setup_<this>.x`).
|
|
170
170
|
* Node 22 is a current active LTS; Node 20 (the old literal) is the OLDEST LTS
|
|
@@ -432,7 +432,10 @@ write_files:
|
|
|
432
432
|
cfg Routing.Type "auto"
|
|
433
433
|
# Re-announce everything we serve; interval well under the record expiry.
|
|
434
434
|
# (Kubo 0.38 uses Provide.*; the pre-0.38 keys FATAL the daemon at boot.)
|
|
435
|
-
|
|
435
|
+
# Provide.Interval is a DURATION value Kubo requires as JSON. Without --json,
|
|
436
|
+
# setting it errors (not found / maybe use --json) and, under set -e, aborts
|
|
437
|
+
# this whole setup script (leaving the box half-provisioned).
|
|
438
|
+
cfg --json Provide.Interval '"12h"'
|
|
436
439
|
cfg Provide.Strategy "all"
|
|
437
440
|
|
|
438
441
|
# --- Resource hygiene for a small box ---
|
|
@@ -468,6 +471,12 @@ write_files:
|
|
|
468
471
|
User=ipfs
|
|
469
472
|
Group=ipfs
|
|
470
473
|
Environment=IPFS_PATH=/var/lib/ipfs/.ipfs
|
|
474
|
+
# Guarantee the datadir exists (owned by ipfs) BEFORE the sandboxed daemon
|
|
475
|
+
# starts: ReadWritePaths=/var/lib/ipfs requires the path to exist at unit
|
|
476
|
+
# start, so a missing datadir otherwise fails namespace setup with
|
|
477
|
+
# 226/NAMESPACE and crash-loops. The leading '+' runs this as root, outside
|
|
478
|
+
# the sandbox; it is idempotent.
|
|
479
|
+
ExecStartPre=+/usr/bin/install -d -o ipfs -g ipfs /var/lib/ipfs
|
|
471
480
|
ExecStart=/usr/local/bin/ipfs daemon --migrate=true --enable-gc
|
|
472
481
|
Restart=on-failure
|
|
473
482
|
RestartSec=5
|