pinnace 0.1.0 → 0.2.0
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 +155 -6
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +245 -20
- package/dist/cli/run.js.map +1 -1
- package/dist/node/node-commands.js +9 -5
- package/dist/node/node-commands.js.map +1 -1
- package/dist/provision/cloud-init.d.ts +25 -0
- package/dist/provision/cloud-init.d.ts.map +1 -1
- package/dist/provision/cloud-init.js +77 -7
- package/dist/provision/cloud-init.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/run.ts +336 -20
- package/src/node/node-commands.ts +9 -5
- package/src/provision/cloud-init.ts +102 -7
|
@@ -313,11 +313,15 @@ async function defaultWarm(
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
/**
|
|
316
|
-
* Default `status
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
316
|
+
* Default `status` — a thin stand-in used ONLY when no `status` op is injected.
|
|
317
|
+
* The PRODUCTION on-box path (`pinnace node status`, wired in cli/run.ts's
|
|
318
|
+
* `runNodeCli`) injects the OWNED `status` op ({@link makeStatusOp} from
|
|
319
|
+
* status-report) via {@link NodeCommandContext.ops}.status, which is the real
|
|
320
|
+
* per-site CID/IPNS/announce/gateway report. This default remains as the
|
|
321
|
+
* seam's safe fallback (e.g. a direct `runNodeCommand` call that does not want
|
|
322
|
+
* the live external checks / a hermetic test that injects its own op), so a
|
|
323
|
+
* bare call still reports current CID + IPNS id without reaching the network.
|
|
324
|
+
* The dashboard write is done by the command layer ({@link runNodeCommand}).
|
|
321
325
|
*/
|
|
322
326
|
async function defaultStatus(
|
|
323
327
|
ctx: NodeCommandContext,
|
|
@@ -41,6 +41,18 @@
|
|
|
41
41
|
* (interval well under the record-expiry window, strategy `all`).
|
|
42
42
|
* - Caddy HTTPS reverse proxy for the RPC API (auto TLS), which forwards the
|
|
43
43
|
* operator's bearer header straight through to the token-guarded Kubo API.
|
|
44
|
+
*
|
|
45
|
+
* INSTALL CHANNEL + BOOT-SAFETY (task `cloud-init-pinnace-install-channel`):
|
|
46
|
+
* - The box installs a PINNED `pinnace` ({@link DEFAULT_PINNACE_VERSION}) on a
|
|
47
|
+
* named current Node LTS ({@link DEFAULT_NODE_MAJOR}), both overridable per
|
|
48
|
+
* box. Pinned (never floating `latest`) so a boot is reproducible.
|
|
49
|
+
* - The install is BOOT-SAFE: `pinnace-setup.sh` is invoked with `|| true`, so
|
|
50
|
+
* a transient npm/registry failure can NOT abort the boot. Kubo, the
|
|
51
|
+
* firewall and Caddy come up regardless of the agent install.
|
|
52
|
+
* - The dedicated `ipfs` service user is created by cloud-init's `users:`
|
|
53
|
+
* module, which runs BEFORE `runcmd`, so no boot step can hit "invalid user
|
|
54
|
+
* 'ipfs'" (closes the first-boot race in
|
|
55
|
+
* work/notes/observations/cloud-init-first-boot-ipfs-user-race-and-set-e-abort.md).
|
|
44
56
|
*/
|
|
45
57
|
import type {HostRole} from '../config/config-resolution.js';
|
|
46
58
|
|
|
@@ -95,6 +107,19 @@ export interface ProvisionInput {
|
|
|
95
107
|
gateways?: string[];
|
|
96
108
|
/** The Kubo version to install. Defaults to a pinned known-good release. */
|
|
97
109
|
kuboVersion?: string;
|
|
110
|
+
/**
|
|
111
|
+
* The `pinnace` version to install on the box (`npm install -g
|
|
112
|
+
* pinnace@<this>`). Defaults to {@link DEFAULT_PINNACE_VERSION} (the current
|
|
113
|
+
* published release). PINNED, never floating `latest`, so a box boot is
|
|
114
|
+
* reproducible. Overridable per-box (e.g. to roll a box onto a newer agent).
|
|
115
|
+
*/
|
|
116
|
+
pinnaceVersion?: string;
|
|
117
|
+
/**
|
|
118
|
+
* The Node.js major version to install via NodeSource (`setup_<this>.x`).
|
|
119
|
+
* Defaults to {@link DEFAULT_NODE_MAJOR} (a current active LTS). A named knob
|
|
120
|
+
* so the LTS bump is one obvious edit, not a literal buried in a shell line.
|
|
121
|
+
*/
|
|
122
|
+
nodeMajor?: string;
|
|
98
123
|
/** The MFS directory sites live under. Defaults to `/sites`. */
|
|
99
124
|
sitesDir?: string;
|
|
100
125
|
}
|
|
@@ -131,6 +156,23 @@ export interface HostProvider {
|
|
|
131
156
|
|
|
132
157
|
/** Defaults kept in one place so the template + docs never drift. */
|
|
133
158
|
const DEFAULT_KUBO_VERSION = 'v0.38.1';
|
|
159
|
+
/**
|
|
160
|
+
* The pinned `pinnace` version the box installs (`npm install -g
|
|
161
|
+
* pinnace@<this>`). Mirrors {@link DEFAULT_KUBO_VERSION}: a NAMED knob, not a
|
|
162
|
+
* literal, so a release bump is one obvious edit here. PINNED (never floating
|
|
163
|
+
* `latest`) so a box boot is reproducible: the same cloud-init always installs
|
|
164
|
+
* the same agent. Overridable per-box via {@link ProvisionInput.pinnaceVersion}.
|
|
165
|
+
* `pinnace@0.1.0` is the first published release (npm, public, OIDC provenance).
|
|
166
|
+
*/
|
|
167
|
+
const DEFAULT_PINNACE_VERSION = '0.2.0';
|
|
168
|
+
/**
|
|
169
|
+
* The pinned Node.js major the box installs via NodeSource (`setup_<this>.x`).
|
|
170
|
+
* Node 22 is a current active LTS; Node 20 (the old literal) is the OLDEST LTS
|
|
171
|
+
* (EOL ~2026-04) and incoherent with the repo's own Node 24 toolchain. A NAMED
|
|
172
|
+
* knob (mirrors {@link DEFAULT_KUBO_VERSION}) so the LTS bump is one edit.
|
|
173
|
+
* Overridable per-box via {@link ProvisionInput.nodeMajor}.
|
|
174
|
+
*/
|
|
175
|
+
const DEFAULT_NODE_MAJOR = '22';
|
|
134
176
|
const DEFAULT_SITES_DIR = '/sites';
|
|
135
177
|
const DEFAULT_GATEWAYS: readonly string[] = [
|
|
136
178
|
'https://{cid}.ipfs.dweb.link/',
|
|
@@ -239,6 +281,8 @@ function renderHetznerCloudInit(input: ProvisionInput): string {
|
|
|
239
281
|
input.role === 'replica' ? (input.publisherEndpoint ?? '') : '';
|
|
240
282
|
const gateways = input.gateways ?? DEFAULT_GATEWAYS;
|
|
241
283
|
const kuboVersion = input.kuboVersion ?? DEFAULT_KUBO_VERSION;
|
|
284
|
+
const pinnaceVersion = input.pinnaceVersion ?? DEFAULT_PINNACE_VERSION;
|
|
285
|
+
const nodeMajor = input.nodeMajor ?? DEFAULT_NODE_MAJOR;
|
|
242
286
|
const sitesDir = input.sitesDir ?? DEFAULT_SITES_DIR;
|
|
243
287
|
const warmGateways = gateways.join(' ');
|
|
244
288
|
|
|
@@ -278,6 +322,22 @@ packages:
|
|
|
278
322
|
- debian-archive-keyring
|
|
279
323
|
- apt-transport-https
|
|
280
324
|
|
|
325
|
+
# ---------------------------------------------------------------------------
|
|
326
|
+
# Service users. cloud-init's \`users:\` module runs BEFORE \`runcmd\`, so the
|
|
327
|
+
# dedicated \`ipfs\` user is GUARANTEED to exist before any boot step uses it
|
|
328
|
+
# (e.g. \`install -o ipfs ...\`). Creating it here (not mid-\`ipfs-setup.sh\`,
|
|
329
|
+
# which is a \`set -e\` block that could abort before its \`useradd\`) closes the
|
|
330
|
+
# first-boot race where a later step hit "invalid user 'ipfs'".
|
|
331
|
+
# \`default\` keeps cloud-init's normal login user; we only ADD \`ipfs\`.
|
|
332
|
+
# ---------------------------------------------------------------------------
|
|
333
|
+
users:
|
|
334
|
+
- default
|
|
335
|
+
- name: ipfs
|
|
336
|
+
system: true
|
|
337
|
+
home: /var/lib/ipfs
|
|
338
|
+
shell: /usr/sbin/nologin
|
|
339
|
+
lock_passwd: true
|
|
340
|
+
|
|
281
341
|
# ---------------------------------------------------------------------------
|
|
282
342
|
# On-box environment consumed by the pinnace node timers + the setup scripts.
|
|
283
343
|
# ---------------------------------------------------------------------------
|
|
@@ -300,6 +360,21 @@ write_files:
|
|
|
300
360
|
# MFS directory that holds your sites (one entry per site).
|
|
301
361
|
SITES_DIR="${sitesDir}"
|
|
302
362
|
|
|
363
|
+
# On-box PATHS the \`pinnace node\` verbs read (they assemble their
|
|
364
|
+
# NodeCommandContext from these keys):
|
|
365
|
+
# DASHBOARD_DIR - where \`node status\` writes status.json (the dashboard
|
|
366
|
+
# vhost \${DASH_DOMAIN} serves this dir).
|
|
367
|
+
# RECORDS_DIR - where \`node republish\` (publisher) EXPORTS the signed
|
|
368
|
+
# record; UNDER the dashboard dir so it is served at
|
|
369
|
+
# \${DASH_DOMAIN}/records/<id>.ipns-record, exactly where a
|
|
370
|
+
# replica's PUBLISHER_ENDPOINT + /records/ fetch looks.
|
|
371
|
+
# CACHE_DIR - where \`node mirror\` (replica) CACHES the last good
|
|
372
|
+
# record for the publisher-outage fallback (under the
|
|
373
|
+
# ipfs user's writable home).
|
|
374
|
+
DASHBOARD_DIR="/var/www/ipfs-dash"
|
|
375
|
+
RECORDS_DIR="/var/www/ipfs-dash/records"
|
|
376
|
+
CACHE_DIR="/var/lib/ipfs/records-cache"
|
|
377
|
+
|
|
303
378
|
# Role: "publisher" holds IPNS keys and signs/exports records; "replica"
|
|
304
379
|
# holds NO key and MIRRORS the publisher's signed records. The pinnace node
|
|
305
380
|
# timers self-gate on this, so scheduling all timers on every box is safe.
|
|
@@ -310,6 +385,12 @@ write_files:
|
|
|
310
385
|
|
|
311
386
|
KUBO_VERSION="${kuboVersion}"
|
|
312
387
|
|
|
388
|
+
# Node.js major installed via NodeSource (setup_<major>.x) and the PINNED
|
|
389
|
+
# pinnace version installed on the box. Both are reproducible: the same
|
|
390
|
+
# cloud-init always installs the same agent on the same runtime.
|
|
391
|
+
NODE_MAJOR="${nodeMajor}"
|
|
392
|
+
PINNACE_VERSION="${pinnaceVersion}"
|
|
393
|
+
|
|
313
394
|
# -- Kubo installer / initializer -----------------------------------------
|
|
314
395
|
- path: /usr/local/sbin/ipfs-setup.sh
|
|
315
396
|
permissions: "0755"
|
|
@@ -408,13 +489,19 @@ write_files:
|
|
|
408
489
|
content: |
|
|
409
490
|
#!/usr/bin/env bash
|
|
410
491
|
set -euo pipefail
|
|
411
|
-
|
|
412
|
-
#
|
|
492
|
+
source /etc/pinnace-node.env
|
|
493
|
+
# Install Node.js (for npm) then the PINNED pinnace binary globally. The
|
|
494
|
+
# on-box timers invoke \`pinnace node <verb>\`: one codebase, client +
|
|
495
|
+
# on-box. NODE_MAJOR / PINNACE_VERSION come from /etc/pinnace-node.env so
|
|
496
|
+
# the runtime + agent version are reproducible and one-edit overridable.
|
|
497
|
+
# This script is invoked NON-FATALLY at boot (\`|| true\`): a transient
|
|
498
|
+
# npm/registry hiccup must NOT abort provisioning (Kubo, the firewall and
|
|
499
|
+
# Caddy are already up by the time this runs). Re-run it manually to retry.
|
|
413
500
|
if ! command -v npm >/dev/null 2>&1; then
|
|
414
|
-
curl -fsSL https://deb.nodesource.com/
|
|
501
|
+
curl -fsSL "https://deb.nodesource.com/setup_\${NODE_MAJOR}.x" | bash -
|
|
415
502
|
apt-get install -y nodejs
|
|
416
503
|
fi
|
|
417
|
-
npm install -g pinnace
|
|
504
|
+
npm install -g "pinnace@\${PINNACE_VERSION}"
|
|
418
505
|
pinnace version
|
|
419
506
|
|
|
420
507
|
# -- Caddy reverse proxy for the HTTPS API (auto TLS) ---------------------
|
|
@@ -490,12 +577,20 @@ runcmd:
|
|
|
490
577
|
# Configure the HTTPS API proxy (no-op if API_DOMAIN unset)
|
|
491
578
|
- /usr/local/sbin/write-caddyfile.sh
|
|
492
579
|
|
|
493
|
-
#
|
|
494
|
-
|
|
580
|
+
# Dashboard dir, owned by the ipfs service user (guaranteed to exist: created
|
|
581
|
+
# by the \`users:\` module above, BEFORE runcmd). Done BEFORE the pinnace
|
|
582
|
+
# install so a transient install failure can never skip it.
|
|
583
|
+
- install -d -o ipfs -g ipfs /var/www/ipfs-dash
|
|
584
|
+
|
|
585
|
+
# Install the pinned pinnace binary (the box runs the same CLI as the client).
|
|
586
|
+
# BOOT-SAFE: \`|| true\` so a transient npm/registry failure does NOT abort the
|
|
587
|
+
# boot (Kubo, the firewall and Caddy are already up). Re-run
|
|
588
|
+
# /usr/local/sbin/pinnace-setup.sh manually to retry; the timers below pick it
|
|
589
|
+
# up on their next tick once the binary is present.
|
|
590
|
+
- /usr/local/sbin/pinnace-setup.sh || true
|
|
495
591
|
|
|
496
592
|
# Enable the pinnace node timers. republish self-gates to publisher, mirror to
|
|
497
593
|
# replica, so enabling all of them on every box is safe (ADR-0002).
|
|
498
|
-
- install -d -o ipfs -g ipfs /var/www/ipfs-dash
|
|
499
594
|
${enableTimers}
|
|
500
595
|
|
|
501
596
|
# =============================================================================
|