lsh-framework 3.6.0 → 3.7.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 CHANGED
@@ -115,6 +115,21 @@ lsh sync push --env dev
115
115
 
116
116
  If exactly one remote service is configured, `lsh` uses it automatically and `LSH_PIN_SERVICE` is optional.
117
117
 
118
+ ### Quickest: bundled pinner (just a token)
119
+
120
+ Skip the manual `ipfs pin remote service add`. Set **`LSH_PIN_TOKEN`** and `lsh` auto-registers a
121
+ remote pinning service for you on first push — defaulting to **4EVERLAND** (free 5GB, standard
122
+ Pinning Service API):
123
+
124
+ ```bash
125
+ # Get a free accessToken from the 4EVERLAND "4EVER Pin" page (https://4everland.org)
126
+ export LSH_PIN_TOKEN=<your-4everland-accessToken>
127
+ lsh push --env dev # auto-registers "lsh-pin" → https://api.4everland.dev, then pins
128
+ ```
129
+
130
+ Use a different provider by overriding the endpoint: `export LSH_PIN_ENDPOINT=<psa-endpoint>`.
131
+ (Note: Pinata's pin-by-CID PSA is paid-only; 4EVERLAND and Filebase offer it free.)
132
+
118
133
  ## Installation
119
134
 
120
135
  ### Prerequisites
@@ -331,6 +346,11 @@ LSH_PIN_SERVICE=<service-name>
331
346
  # Default 'w3name,ipns': durable w3name (signed IPNS via name.web3.storage, no
332
347
  # account, no DHT TTL) with IPNS-over-DHT fallback. Set 'ipns' for DHT-only.
333
348
  LSH_DISCOVERY=w3name,ipns
349
+
350
+ # Optional - bundled pinner: with a token set, lsh auto-registers a remote pin
351
+ # service so pushed content is durable. Endpoint defaults to 4EVERLAND (free 5GB).
352
+ LSH_PIN_TOKEN=<psa-access-token>
353
+ LSH_PIN_ENDPOINT=https://api.4everland.dev # override for another PSA provider
334
354
  ```
335
355
 
336
356
  ### Configuration Files
@@ -36,6 +36,12 @@ export const ENV_VARS = {
36
36
  // Discovery backend(s) for the key→CID pointer, comma-separated in priority
37
37
  // order. Supported: 'w3name' (durable, hosted), 'ipns' (DHT). Default: 'w3name,ipns'.
38
38
  LSH_DISCOVERY: 'LSH_DISCOVERY',
39
+ // Access token for a bundled IPFS remote pinning service. When set (and no
40
+ // remote pin service is already configured), lsh auto-registers one so pushed
41
+ // content is durably pinned. Endpoint defaults to 4EVERLAND (free 5GB).
42
+ LSH_PIN_TOKEN: 'LSH_PIN_TOKEN',
43
+ // Override the PSA endpoint used with LSH_PIN_TOKEN (any IPFS Pinning Service).
44
+ LSH_PIN_ENDPOINT: 'LSH_PIN_ENDPOINT',
39
45
  // Feature flags
40
46
  LSH_LOCAL_STORAGE_QUIET: 'LSH_LOCAL_STORAGE_QUIET',
41
47
  LSH_V1_COMPAT: 'LSH_V1_COMPAT',
@@ -148,4 +154,8 @@ export const DEFAULTS = {
148
154
  IPNS_KEY_DERIVATION_CONTEXT: 'lsh-ipns-v1',
149
155
  // Default discovery backends (priority order): durable w3name, then DHT-IPNS fallback.
150
156
  DISCOVERY_BACKENDS: 'w3name,ipns',
157
+ // Bundled remote pinning service (used with LSH_PIN_TOKEN). 4EVERLAND free tier (5GB),
158
+ // standard IPFS Pinning Service API. Override the endpoint via LSH_PIN_ENDPOINT.
159
+ DEFAULT_PIN_ENDPOINT: 'https://api.4everland.dev',
160
+ DEFAULT_PIN_SERVICE_NAME: 'lsh-pin',
151
161
  };
@@ -15,7 +15,23 @@ import * as path from 'path';
15
15
  import * as os from 'os';
16
16
  import { createLogger } from './logger.js';
17
17
  import { extractErrorMessage } from './lsh-error.js';
18
- import { ENV_VARS } from '../constants/config.js';
18
+ import { ENV_VARS, DEFAULTS } from '../constants/config.js';
19
+ /**
20
+ * Pure helper: choose which configured remote pinning service to use.
21
+ * - explicit `LSH_PIN_SERVICE` wins, but only if it's actually configured;
22
+ * - else the bundled default service (`lsh-pin`) if present;
23
+ * - else the sole configured service;
24
+ * - else null (none / ambiguous).
25
+ */
26
+ export function chooseRemoteService(services, explicit, defaultName) {
27
+ if (explicit) {
28
+ return services.includes(explicit) ? explicit : null;
29
+ }
30
+ if (services.includes(defaultName)) {
31
+ return defaultName;
32
+ }
33
+ return services.length === 1 ? services[0] : null;
34
+ }
19
35
  const logger = createLogger('IPFSSync');
20
36
  /**
21
37
  * Native IPFS Sync
@@ -369,12 +385,43 @@ export class IPFSSync {
369
385
  * - Returns null when nothing is configured or the choice is ambiguous.
370
386
  */
371
387
  async resolveRemoteService() {
388
+ await this.ensureDefaultPinService();
372
389
  const services = await this.listRemoteServices();
373
- const explicit = process.env[ENV_VARS.LSH_PIN_SERVICE];
374
- if (explicit) {
375
- return services.includes(explicit) ? explicit : null;
390
+ return chooseRemoteService(services, process.env[ENV_VARS.LSH_PIN_SERVICE], DEFAULTS.DEFAULT_PIN_SERVICE_NAME);
391
+ }
392
+ /**
393
+ * Bundled pinner: when LSH_PIN_TOKEN is set and no remote pin service named
394
+ * `lsh-pin` is registered yet, auto-register one (endpoint defaults to
395
+ * 4EVERLAND, override via LSH_PIN_ENDPOINT). Lets users get durable pinning
396
+ * with just a token — no manual `ipfs pin remote service add`. Best-effort;
397
+ * never throws. Respects an existing service of the same name and the
398
+ * explicit LSH_PIN_SERVICE (handled by chooseRemoteService).
399
+ */
400
+ async ensureDefaultPinService() {
401
+ const token = process.env[ENV_VARS.LSH_PIN_TOKEN];
402
+ if (!token)
403
+ return;
404
+ const name = DEFAULTS.DEFAULT_PIN_SERVICE_NAME;
405
+ try {
406
+ const existing = await this.listRemoteServices();
407
+ if (existing.includes(name))
408
+ return;
409
+ const endpoint = process.env[ENV_VARS.LSH_PIN_ENDPOINT] || DEFAULTS.DEFAULT_PIN_ENDPOINT;
410
+ const url = `${this.LOCAL_IPFS_API}/pin/remote/service/add` +
411
+ `?arg=${encodeURIComponent(name)}` +
412
+ `&arg=${encodeURIComponent(endpoint)}` +
413
+ `&arg=${encodeURIComponent(token)}`;
414
+ const response = await fetch(url, { method: 'POST', signal: AbortSignal.timeout(10000) });
415
+ if (response.ok) {
416
+ logger.info(`📌 Registered bundled pin service "${name}" → ${endpoint}`);
417
+ }
418
+ else {
419
+ logger.warn(`Could not register pin service "${name}": ${await response.text()}`);
420
+ }
421
+ }
422
+ catch (error) {
423
+ logger.warn(`Pin service registration error: ${extractErrorMessage(error)}`);
376
424
  }
377
- return services.length === 1 ? services[0] : null;
378
425
  }
379
426
  /**
380
427
  * Pin a CID to a configured remote pinning service so the content survives
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lsh-framework",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Simple, cross-platform encrypted secrets manager with automatic sync, IPFS audit logs, and multi-environment support. Just run lsh sync and start managing your secrets.",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {