swarm-deploy 0.3.1 → 0.4.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/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## Unreleased
6
+
7
+ ## 0.4.1
8
+
9
+ - Release fix only; there is no runtime, API, or wire change. The `v0.4.0` publish workflow failed during action setup because its SHA-pinned `holepunchto/actions/publish` referenced `create-release@v1`, which no longer exists at `v1`. The `v0.4.0` tag is left as it is; use 0.4.1, which carries the 0.4.0 changes below.
10
+ - The CI and publish workflows now use the supported `holepunchto/actions/*@v1` pointer instead of commit SHAs, and publishing goes through the `publish@v1` alias. `npm run test:release-tag` now requires `@v1` on every Holepunch action reference. Tag and package validation, the `main` ancestry check, build-before-publish, the package checks, the `npm` environment, and OIDC provenance are unchanged.
11
+ - Documentation: the README is now a short entry point (features, requirements, install, identity model, quick start, and links). The detailed CLI, runtime API, operations and storage, artifact pattern, managed symlink, and hooks references moved to guides under `docs/` on GitHub, which the npm package does not include; the README links to them by URL. The protocol specification is unchanged.
12
+
13
+ ## 0.4.0
14
+
15
+ - Add the optional `afterLink` server hook (`ServerOptions.hooks`, and `--hooks <module>` modules) so a link change can be published. It runs after a managed link is durable, for an upload that moved an automatic link (after `afterCommit`, once per link that now points at the uploaded artifact, including an already-committed retry) and for an explicit `Client.link` / `swarm-deploy link` that passed every policy check. Its context is the secret-free, frozen `{ link: { name, target, mode, path, targetPath }, artifact, status, trigger }`, exported as `AfterLinkContext`, `HookLink`, `HookLinkMode`, `HookLinkStatus` and `HookLinkTrigger`.
16
+ - The client receives no success (`COMMITTED`, `ALREADY_COMMITTED`, `LINKED`, `UNCHANGED`) until the applicable `afterLink` calls return. A failing hook reaches the caller as the existing sanitized `HOOK_FAILED` code in the ordinary `FAILED` link result, upload final, or admission record, so clients and the wire format are unchanged, and `swarm-deploy link` now prints `HOOK_FAILED`. The caller sees that code only while it is still waiting: a client waits at most its idle timeout (60 s by default, 5 min at most) for the whole answer, which covers verification and commit, `afterCommit`, every `afterLink` call one after another, hooks queued ahead for the same link, and retention. If that expires first the client reports `UPLOAD_IDLE_TIMEOUT` instead, while the server still finishes its hooks. The durable link and artifact are never rolled back. Hooks must be idempotent on `link.name` plus `artifact.transferId`: repeating an unchanged link request (`UNCHANGED`) or retrying an upload re-runs the hook, so a failed publication can be retried without changing the link.
17
+ - A failed `afterLink` is observable server-side: `onFailure` gets the new `afterLink` `HookFailureContext.phase` (with `link`, and the link path as `path`) after the client was answered, a `failure` event and a `Link hook failed` warning are emitted, and a failed link request's `link` event carries the new optional `result` (`LINKED` or `UNCHANGED`) with `reason: 'HOOK_FAILED'`.
18
+ - Hooks run after the storage-root lease is released, so a slow hook never blocks commits, retention, recovery, or other links and cannot deadlock on the lease. The context is therefore a snapshot: a later decision may already have repointed the link, and retention may have removed an unpinned previous target. For one link name, hooks start in decision order and never overlap (an in-memory first-in, first-out line reserved under the lease; it does not survive a restart, and there is still no hook timeout). Hooks do not run for rejected or unauthorized link requests, for a manual link whose target is replaced, or for startup, scheduled, or retention reconciliation.
19
+ - **Fix.** After a `HOOK_FAILED` answer (any hook phase) the server now ends its write side and destroys the connection once the peer closes it, or after 5 seconds, instead of destroying it right behind the write. Over a real stream the immediate destroy could reset the connection before the client read the record, so the caller saw a connection reset instead of `HOOK_FAILED`. Other failure answers still close immediately and are unchanged.
20
+ - Configuring `afterLink` defers the post-commit retention pass until the hooks succeed, like `afterCommit`, so this commit's own link reconciliation runs before this commit's retention pass. That narrows, but does not remove, the chance that the link already moved: another commit's retention or reconciliation can do it first, and the hook then sees `UNCHANGED`. `status` is the link decision the call observed (`LINKED` when it created or repointed the link or repaired an automatic link's ownership record, `UNCHANGED` otherwise, including a manual ownership-metadata refresh for the same target), never proof of publication. Servers without `afterLink` are unchanged, including `afterCommit` ordering and its deferred retention.
21
+ - Add the runnable, **Node-only** `examples/link-publisher` hooks module and publisher (they use `node:child_process` and the global `process`, so they do not run on Bare; library hooks themselves work on Node and Bare). Each link change is passed as one JSON document on stdin to a script started without a shell: `SWARM_DEPLOY_PUBLISHER` names a JavaScript module file run with the current Node executable, not an arbitrary executable. Each call has a 20 s deadline: the publisher then gets `SIGTERM` and, 5 s later, `SIGKILL` (that direct child only), and the call always rejects with a timeout, even if the publisher exits zero after `SIGTERM`. It settles only once the child has exited, so an abandoned publisher cannot write over the next publication of the same link, and a call takes at most 25 s. The example's header shows how to size the client idle timeout for several sequential calls (25 s each).
22
+ - Add `swarm-deploy reset-storage --storage <dir> [--apply [--wait <ms>]]`. Without `--apply` it is a read-only preview of the
23
+ private state under one storage root: commit records and how many have no
24
+ payload, sessions, journals, staging, publications, the managed-link ledger,
25
+ trash, lock residue, earlier archives, and the visible payloads and symlinks
26
+ that would remain. With `--apply` it takes the storage lock, renames those
27
+ directories and lock residue into
28
+ `.swarm-deploy/archive/reset-<timestamp>-<random>/` with a `reset.json`
29
+ marker, recreates them empty, and releases the lock. Nothing is copied or
30
+ deleted; an archive is restored by moving it back.
31
+ - The reset never signals a process. A live lock holder makes `--apply` fail
32
+ closed with exit `1`; `--wait` polls until the holder releases the lock, a
33
+ holder whose PID is gone is retired automatically. Live and unreadable
34
+ locks cannot be bypassed by a PID override.
35
+ - The report states what still blocks a true fresh start: remaining payloads
36
+ keep their names (`FILE_EXISTS`) and remaining symlinks lose their ownership
37
+ record (`LINK_CONFLICT`). `fresh start: yes` is printed only for a root with
38
+ nothing visible left. Unsafe roots, symlinked internal paths, filesystem
39
+ roots, and uninitialized roots are rejected with exit `2` before any change.
40
+ - Block server recovery after an interrupted reset until a reset retry
41
+ completes, reject symlink ancestors, and refresh retained-file reporting
42
+ after waiting for exclusive access.
43
+ - A retry after an interrupted reset, including a crash or power loss that
44
+ skips error handling, resumes the archive the pending marker names instead of
45
+ creating a second one. Progress is reconciled from what the archive holds,
46
+ and a retry fails closed when the archive and the live directory both hold
47
+ data or the named archive is gone. One reset always ends as one archive, so
48
+ restoring it restores everything.
49
+ - Keep the archive marker bounded. `reset.json` is version 2: it names the
50
+ metadata directories, summarizes lock residue as a count and a SHA-256 of the
51
+ sorted names, and keeps the first attempt, the newest 16 attempts, and a total
52
+ count with failure text clipped. A marker therefore stays readable after any
53
+ number of stopped servers or retries, instead of becoming unreadable once its
54
+ lock list outgrew the reader. Version 1 markers stay readable at the shared
55
+ metadata size bound, a retry that resumes one first preserves it verbatim as
56
+ `reset.v1.json`, and the preview counts lock residue rather than listing it.
57
+ - Document that `reset-storage` must run as the server's operating-system user:
58
+ applying it as another user, such as `root`, leaves metadata the service
59
+ cannot use. The command does not enforce this.
60
+ - Export `resolveLayout`, `inspectStorageLock`, and `isProcessAlive` from the
61
+ storage layout module for the reset; existing lock behaviour is unchanged.
62
+
5
63
  ## 0.3.1
6
64
 
7
65
  - Normalize verified published artifact permissions before they become visible: