• DcRouter v32.6.3
    Release / build-and-release (push) Successful in 21m45s
    Stable

    jkunz released this 2026-09-29 05:07:41 +00:00 | 0 commits to main since this release

    2026-09-29 - 32.6.3

    Maintenance

    • Release tooling: @git.zone/tsdeno ^1.11.0 → ^1.12.0, @git.zone/tsdocker ^3.8.1 → ^3.9.0, @git.zone/tswatch ^4.0.1 → ^4.0.2 and @git.zone/tsrun ^3.0.1 → ^3.0.2; none needs a migration. tsdeno 1.12.0 reads the module graph with deno info before each compile and fails with UNRESOLVED_DEPENDENCY instead of writing a binary without an npm package Deno could not resolve, the failure behind the short 32.6.1 binaries; tsdocker 3.9.0 adds --repository to push, which dcrouter does not use; tswatch 4.0.2 keeps watching after a failed bundle build. tsrun 3.0.2 no longer loads two modules it never used, which starts every tsrun process about a second sooner; tsbuild 5.0.0, tsbundle 3.0.0 and tstest 6.3.2 are current; pnpm 12.5.1 and @types/node 26.6.2 stay until pnpm 12.6.0 (2026-09-29 17:08 UTC) and @types/node 26.6.3 (2026-10-02) are seven days old.
    • @design.estate/dees-catalog ^16.6.0 → ^16.12.0 (locked 16.6.0 → 16.12.0), which brings @design.estate/dees-element 3.3.0 → 3.4.0; the tree keeps one copy of each. The Ops UI binds no frameless and uses neither the stepper's removed scroller nor setScrollStatus(), so no source changes. What looks different comes from the catalog: the login screen (dees-simple-login) drops its card surface and page backdrop, the bootstrap and email-domain steppers show the new progress ribbon with a close control in the step header, and dees-input-list rows take the reworked layout.
    • The SmartDB upgrade/rollback test gives its four restart phases one shared 50 s budget, inside the suite's 60 s per-file limit, instead of 20 s each, so one phase slowed by host load no longer fails a file that still has time; a timeout names the phase that ran out. It starts the fixture with tsrun directly rather than through pnpm exec, which did not forward SIGTERM, so a timed-out fixture no longer outlives the test and re-creates its work directory after cleanup.
    Downloads
  • DcRouter v32.6.2
    Release / build-and-release (push) Successful in 22m2s
    Stable

    jkunz released this 2026-09-28 02:50:05 +00:00 | 5 commits to main since this release

    2026-09-28 - 32.6.2

    Fixes

    • Move the in-process RemoteIngress hub to @serve.zone/remoteingress ^32.0.3 (from ^32.0.0, locked at 32.0.0), whose QUIC control stream is read cancel-safely on both sides. Up to 32.0.2 a control message was read as a 5-byte header and then its payload with two read_exact calls inside a tokio::select!, and written in two writes that could arrive in separate packets; when another branch won after the header was consumed, the next read decoded the payload as a header. A split CTRL_UDP_SESSION_CLOSE frame made the edge refuse control message too large: <N> bytes (max 65536) and tear its tunnel down, dropping every port listener of that edge while the hub logged control stream error: connection lost - the teardowns seen on the SSH tunnel to code.foss.global:29419. From 32.0.3 the hub keeps a partially received control message buffered across cancelled reads and writes each message as one buffer, so it no longer loses a frame an edge sent, and the frames it sends no longer leave the header and payload in separate writes. The hub API dcrouter uses (RemoteIngressHub, its events, start, stop, getStatus, updateAllowedEdges, the egress proxy calls and encodeConnectionToken) is unchanged across 32.0.1-32.0.3, and the wire format is unchanged, so no dcrouter code changes. Edges read the hub's control stream with their own reader: an edge is fully protected only once it runs remoteingress 32.0.3 as well (see the readme's RemoteIngress version note).

    • The release binaries resolve a first-party release published the same day. Deno 2.9 compiles with a 24-hour minimum dependency age by default, which left @serve.zone/remoteingress@^32.0.3 unresolved in deno compile (the smoke check caught it). deno.json now commits the owner's policy that pnpm already applies: seven days for third-party packages, owner scopes (@api.global, @apiclient.xyz, @design.estate, @git.zone, @idp.global, @lossless.org, @push.rocks, @serve.zone, @tempfix, @tsclass) exempt, so every build host compiles under the same age policy whatever its own pnpm configuration (tsdeno 1.11.0 leaves a committed policy untouched). Deno resolves the package.json ranges against the registry at compile time, not pnpm-lock.yaml. The binaries return to their complete size (816 MiB x64, 798 MiB arm64 from 564 npm packages, the size of the June builds): the 32.6.1 binaries (about 475 MiB) were most likely compiled without owner packages published within 24 hours of the build, which the --version smoke check cannot see.

    Maintenance

    • @design.estate/dees-catalog ^16.4.0 → ^16.6.0 (locked 16.5.0 → 16.6.0).
    • Release tooling: @git.zone/tsdeno 1.11.0 and @git.zone/tsbundle 3.0.0 (the dashboard bundle ships dist_serve/bundle.js.third-party-notices.{txt,json}); pnpm 12.6.0 and @types/node 26.6.3 wait for the seven-day rule.
    Downloads
  • DcRouter v32.6.1
    Release / build-and-release (push) Successful in 19m53s
    Stable

    jkunz released this 2026-09-27 21:05:57 +00:00 | 10 commits to main since this release

    2026-09-27 - 32.6.1

    Fixes

    • The Ops UI's application log (Logs > Application Logs) hands the log each entry once. It counted the entries it had passed on, so once the browser held its cap of 2000 entries no new entry reached the log, returning to the tab showed an empty log until the next entry arrived, and a view that was attached again passed every entry a second time. It now remembers the newest entry the log on screen holds and passes the entries after it; a fetched list that replaces the shown one clears the log first, and a log created anew, on returning to the tab, gets the whole list. test/test.logs-view.chromium.ts covers streaming, the cap, a replacing fetch and the return to the tab.
    • ts_web/appstate/runtime.ts imports the platform-events state statically. It loaded it through a dynamic import(), so a bundle evaluated that module lazily and an element that read configEventStatePart while it was constructed, as the logs view does, could find it undefined; the Chromium test bundle did.

    Maintenance

    • Move the Ops UI to @design.estate/dees-catalog ^10.0.0 → ^16.4.0 (16.5.0 in the lockfile), @design.estate/dees-element ^3.1.1 → ^3.3.0 and @serve.zone/catalog ^4.0.0 → ^33.0.0, which renders on dees-catalog 16; the three move together, because two copies of the catalog register the same dees-* elements and the page stops with NotSupportedError. pnpm dedupe leaves one copy each of dees-catalog, dees-element, dees-domtools (3.0.1), lit and @serve.zone/catalog in the bundle; it also drops duplicate resolutions of @types/node 26.6.1, @push.rocks/smartrust 2.0.0, @push.rocks/smartwatch 6.4.3, @design.estate/dees-comms and picomatch. The one source change the catalog needs is in the application log: dees-catalog 14 removed dees-chart-log's terminalReady, and the log keeps entries it is given before its terminal is up, so the view no longer waits for it (see Fixes). The other catalog breaks between 11 and 16 do not touch the Ops UI: it uses dees-simple-appdash rather than dees-appui, gives every dees-chart-area its own yAxisFormatter, reads no seriesStats, binds no vintegrated and uses the sz-* elements, whose API did not change from @serve.zone/catalog 4 to 33. What looks different comes from the catalog: button glyph size and inset (15.2.0), stats-grid titles that break between words (16.3.0), the log on xterm 6 with the theme's terminal palette (14.0.0), and Enter in the last field of a dialog's form now runs the dialog's last action (16.3.0).

    • Bring release tooling current: @git.zone/tstest ^6.3.0 → ^6.3.2 (a Node.js test file that runs past --timeout now fails even when it handles SIGTERM and exits cleanly), @git.zone/tsrun ^3.0.0 → ^3.0.1 and @git.zone/tsdeno ^1.8.0 → ^1.10.0; tsbuild 5.0.0, tsbundle 2.15.0, tsdocker 3.8.1 and tswatch 4.0.1 were already current, and pnpm 12.5.1 and @types/node 26.6.2 stay until pnpm 12.6.0 and @types/node 26.6.3 are seven days old. None of the three needs a migration. With tsdeno 1.10.0 the release binaries leave out the native engines built for the other architecture and for macOS (tsdeno 1.9.0's default per-target pruning): on Deno 2.9.7 dcrouter-linux-x64 is 498,262,576 bytes and dcrouter-linux-arm64 489,086,376 bytes, down from 984,393,616 and 975,118,288 bytes in the 32.6.0 release. The compile targets in .smartconfig.json now pin Deno with denoVersion 2.9.7 (tsdeno refuses any other version with DENO_VERSION_MISMATCH), fail a binary below a size floor (minSize 355 MiB for x64 and 350 MiB for arm64, about three quarters of the measured size, because deno compile can exit 0 after writing an incomplete binary) and run the x64 binary once with --version in an empty environment, expecting exit code 0 and a version line (the arm64 binary cannot run on the x64 runner and is skipped). The release workflow installs the pinned Deno 2.9.7 from its checksum-verified release archive and puts it first on PATH for the binary build, because the base image's own Deno shadowed denoland/setup-deno: 32.6.0 was compiled with the image's Deno 2.9.6 although setup-deno had installed 2.9.7.

    Downloads
  • DcRouter v32.6.0
    Release / build-and-release (push) Successful in 26m16s
    Stable

    jkunz released this 2026-09-26 17:03:29 +00:00 | 14 commits to main since this release

    2026-09-26 - 32.6.0

    Features

    • Report stored routes that cannot be served. Route warnings (IRouteWarning) gain the type route-ingress-disabled: a stored route whose only ingress paths are off (SmartVPN or the RemoteIngress hub not running) is left out of the applied routes and listed with this warning, instead of stopping every other route.

    Fixes

    • Hold one local mirror row per provider DNS record. Releases before 19.1.0 ran provider-zone syncs, gateway route DNS reconciles and mail DNS reconciles concurrently, so two writers could each mirror the same new provider record; syncDomain() then refuses the zone with Local mirror has duplicate provider record id …, and every automated DNS write for that zone fails as zone-unavailable until the row is removed. DnsRecordDoc now stores providerRecordKey (<domainId>:<providerRecordId>), derived in one place (ts/dns/provider-record-key.ts) by createSavableObject() and by the ACME and testing mirror writers, and cleared with the provider id when a zone migrates to dcrouter. The sparse unique index provider_record_key_unique makes a racing second mirror insert fail instead of persisting. The new migration step reconcile-provider-record-mirror-duplicates (32.0.1 to 32.6.0) keeps the row that carries managedBy ownership and deletes its unowned synced copies, keeps the oldest row where none is owned, trims stored ids, backfills the key and then creates the index. It deletes local mirror rows only and never calls the provider. When rows with different owners share one provider record, it refuses by name and changes nothing. The error lists the domain, the provider id, the row ids and the owners.
    • Start dcrouter when neither SmartVPN nor a RemoteIngress hub runs. Since 17.9.0 every route without an explicit ingress policy, including the generated dns-over-https-dns-query route and email routes, was authorized for SmartVPN, and a remoteIngress route for the edge tunnel, whether or not that runtime was on. The listener therefore got inboundProxyProtocol optional or required while SmartProxy had no trusted proxy, and SmartProxy refused the route set: Route 'dns-over-https-dns-query': inboundProxyProtocol mode optional/required needs trustedProxyIPs or global trustedProxyIPs, a critical startup failure. Ingress origins are now authorized only for the paths whose runtime is on: smartVpn when vpnConfig.enabled, trustedProxy when the RemoteIngress hub is enabled. A listener that only direct traffic reaches gets reject. A route created through the API whose only declared paths are off, for example a VPN-only route on a hub without SmartVPN, is now left out of the applied route set on its own. Every other route is still applied, at startup and on every route application. The route is logged at warn when it is first refused and listed among the route warnings of getMergedRoutes and the Ops UI routes view as route-ingress-disabled. A route from dcrouter's own configuration whose only paths are off still fails startup with Route '<name>' has no enabled ingress path, because it restates the operator's options.
    Downloads
  • DcRouter v32.5.0
    Release / build-and-release (push) Failing after 13m3s
    Stable

    jkunz released this 2026-09-26 09:13:58 +00:00 | 17 commits to main since this release

    2026-09-26 - 32.5.0

    Features

    • Let the ACME settings name another CA's ACME directory. IAcmeConfig, AcmeConfigDoc and updateAcmeConfig carry an optional directoryUrl, which dcrouter passes to SmartAcme in place of Let's Encrypt, for example SmartAcme's own server.AcmeServer on the loopback of a disposable rehearsal host; absent, dcrouter orders from Let's Encrypt as before, and null returns to it. updateAcmeConfig accepts only SmartAcme's own rule, an https: URL or an http: URL on localhost, 127.0.0.1 or [::1], without credentials or fragment, and refuses anything else with the rule it breaks, without repeating the URL. A change is treated like an account change: it is refused while certificate jobs or ACME DNS cleanup are unfinished, and issuance waits until SmartAcme is rebuilt on the new directory. A directory stored past that check, by a direct database write, is refused by SmartAcme at startup and now classified as a permanent acme-account-configuration failure instead of consuming the startup retry budget. The settings dialog under Domains > Certificates gains an ACME directory field and keeps the dialog open with dcrouter's reason when it refuses the settings; the settings tile and the configuration view show the directory, and dcrouter's logs name only its origin. Rows written by earlier builds carry no directory and keep Let's Encrypt, so no migration is needed. test/test.acme-directory.node.ts covers the rule against SmartAcme's own refusal, persistence, reload and clearing, the refusal during unfinished DNS cleanup, a dcrouter that rebuilds SmartAcme from the directory stored through updateAcmeConfig onto two loopback CAs in turn, with an account key per directory, and a route certificate and a gateway-client exact certificate issued by the CA of the stored directory.

    Maintenance

    • Release tooling currency: @git.zone/tstest ^6.2.0 → ^6.3.0 (Chromium test files tear down their browser, servers and timers on every outcome and no longer share one browser in a parallel group; no migration), @types/node 26.6.1 → 26.6.2 and packageManager pnpm@12.4.2 → pnpm@12.5.1. The other @git.zone/* dev tools are already at their current releases. The lockfile keeps @types/node 26.6.1 only as the resolution of four @types/* packages' * range.
    Downloads
  • DcRouter v32.4.0
    Release / build-and-release (push) Successful in 21m9s
    Stable

    jkunz released this 2026-09-25 14:52:19 +00:00 | 20 commits to main since this release

    2026-09-25 - 32.4.0

    Features

    • Serve gateway-client hostname records and exact certificates, the @serve.zone/interfaces 32.21.0 contracts syncGatewayClientDnsRecord, getGatewayClientCertificate and releaseGatewayClientCertificate, and advertise them as dns.clientRecords and certificates.exactIssuance. A gateway client can now hold an exact hostname without a gateway route: one A or AAAA record at an address inside its new allowedDnsRecordAddresses policy field, and one certificate for exactly that name, which Cloudly needs for its cluster relay names. Only gateway-client credentials can call them, with the syncDnsRecords, requestCertificates and readCertificates capabilities, for hostnames inside the client's hostnamePatterns. A hostname has one holder: a testing reservation, an enabled gateway-client route, another client or app, or an address record dcrouter does not own is a conflict, and dcrouter never overwrites or adopts such a record. The route DNS reconciler in turn answers record-conflict for a hostname an ownership holds, also through a certificate alone. Records are managed as gateway-client-hostname under the route reconciler's DNS exclusion. Certificates need a zone with verified authority and are issued by SmartAcme's exact-identifier DNS-01 path in a namespace per client and app; a request answers pending while the issuance runs. A request for a hostname nobody holds yet first synchronizes a provider zone and is refused as hostname-conflict when the name carries an address record dcrouter does not own, or answers issuance-unavailable (retryable) when the synchronization fails; polls for a held hostname synchronize nothing. Releasing a certificate retires its stored material, and a sweep at start and every minute removes the records, certificates and claims of deleted clients, of hostnames outside the client's patterns and of addresses outside its allowance. The allowance is set through provisionGatewayClientCredential, createGatewayClient and updateGatewayClient, stored canonically, and left out of the policy digest while it is empty, so existing credentials keep matching. The exact-certificate issuance now takes its SmartAcme namespace from the caller, and testing grants keep theirs. @serve.zone/interfaces moves from 32.0.0 to 32.21.0 in the lockfile (^32.0.0 → ^32.21.0), which brings @push.rocks/smartlog 4.0.0 as its dependency. test/test.gateway-client-hostname.node.ts covers the record lifecycle, the conflicts in both directions, certificate issuance, release and the sweep against a real database and DNS manager, and the handlers' credential, pattern and capability checks.
    • Show and edit a gateway client's allowedDnsRecordAddresses in the Ops UI under Access > Gateway Clients: a DNS addresses column, a field in the create dialog and the row action Edit DNS Addresses. Both dialogs judge the comma separated list with @serve.zone/interfaces' validateGatewayAllowedDnsRecordAddresses, which the web bundle now imports, keep the dialog open with the reason when it or dcrouter refuses the value, and close only on success. The create dialog previously closed before the request and ignored a refusal, and updateGatewayClientAction ignored an unsuccessful answer; it now records dcrouter's message as the view's error. Changing the allowance through an admin update raises the client's policy generation like any policy change, so its credentials must be provisioned again. test/test.gateway-clients.chromium.ts renders the view and drives the edit dialog through a refused value, a refused update and a saved one.
    • Refuse a testing grant, on request and on approval, for a hostname a gateway-client hostname ownership holds, with ownership_conflict. The ownership is looked up by hostname under the DNS exclusion its writers take, so one that holds only a certificate, with no DNS record, is refused too. test/test.testing-access.node.ts covers the certificate-only case.

    Maintenance

    • Make test/test.email-domain-creation-progress.node.ts "domain observation switches from list discovery to id polling" independent of the wall clock; it failed on every run, also on v32.3.2. The test gave observeNewEmailDomain a 100 ms deadline with 1 ms polls and asserted two list and two id reads. tsx, which runs the test, scans its disk cache synchronously once per process (131,641 entries in the shared cache on the build host) and blocked the event loop for about 110 ms during the first 1 ms wait, so the deadline expired after one list read. observeNewEmailDomain now takes an optional now clock for its deadline (default Date.now), and the test passes a frozen one, so the reads alone decide the result and tstest's timeout still bounds a hang. The module's behaviour with the default clock is unchanged.
    Downloads
  • DcRouter v32.3.2
    Release / build-and-release (push) Successful in 16m51s
    Stable

    jkunz released this 2026-09-24 14:22:04 +00:00 | 22 commits to main since this release

    2026-09-24 - 32.3.2

    Fixes

    • Serve the OpsServer through @api.global/typedserver 13.0.2 (from 11.2.1), with @api.global/typedsocket 8.5.0 (from 8.0.3) and @push.rocks/smartserve 7.2.0 (from 6.2.2), which typedserver 13 and typedsocket 8.5.0 run on. typedserver 11.2.1's UtilityWebsiteServer.stop() threw a TypeError before start(), so the OpsServer tracked whether it had called start() and stopped the web server only then; 13.x resolves that stop(), and the tracking is gone. The OpsServer now injects the live reload client and watches dist_serve only under tswatch (TSWATCH=true), as typedserver 13's UtilityWebsiteServer defaults; 11.2.1 did both in production as well. TypedServer answers the service worker's serviceworker_versionInfo itself with the app hash of the served files, and the OpsServer now declares dcrouter's version from its commit info as the app version, which that answer and /appversion carry (11.2.1 answered x.x.x, later 1.0.0). Without the reload client, production no longer serves typedserver's /typedserver/devtools and /typedserver/reloadcheck, and responses of added routes, /mcp included, carry Cache-Control: no-cache and the app-hash stamp. Otherwise routes, /typedrequest, /mcp, log streams and their authorization, and static serving with the SPA fallback are unchanged.

    Maintenance

    • The lockfile carries one typedserver line for runtime and dev tooling (tswatch 4 already brought typedserver 13 and typedsocket 8.5.0), drops typedserver 11.2.1, typedsocket 8.0.3 and 8.1.0, smartserve 6.2.2 and @push.rocks/smartlog-destination-devtools 1.0.12, which only typedserver 11.2.1 used, and resolves @idp.global/sdk's @push.rocks/smartserve ^6.2.2 to 6.3.1, which meets typedsocket 8.5.0's >=6.2.4 peer range. @api.global/typedrequest stays at 8.0.4, which typedserver 13 and typedsocket 8.5.0 accept.
    • Bundle the web UI with @git.zone/tsbundle 2.15.0 (from 2.14.0), which tstest 6.2.0 and tswatch 4.0.1 resolve to as well, so the lockfile keeps one tsbundle. 2.15.0 changes how includeFiles entries are placed and matched; dcrouter's one entry, ./html/**/*.html, matches only html/index.html, and a clean dist_serve built with 2.15.0 is byte-identical to one built with 2.14.0 (bundle.js, its source map, the worker chunk with its map and artifact manifest, and index.html).
    Downloads
  • DcRouter v32.3.1
    Release / build-and-release (push) Successful in 15m46s
    Stable

    jkunz released this 2026-09-24 12:21:48 +00:00 | 25 commits to main since this release

    2026-09-24 - 32.3.1

    Fixes

    • Pass the ACME renewal threshold to SmartAcme, and consume @push.rocks/smartacme 9.8.1 (from 9.7.0), which honours it. SmartAcme received no threshold and kept a stored certificate until 10 days before expiry, so SmartProxy's daily renewal sweep, which asks from the configured 30 days on, got the old certificate back every day until the 10-day mark. The configured threshold (default 30 days) now governs both: a certificate is renewed once at most that many days, or a third of its lifetime if that is less, remain, and testing certificates carry the same margin in renewAfter. A failed renewal keeps the current certificate stored. updateAcmeConfig refuses a threshold that is not a positive number of days, because SmartAcme refuses it at construction. One already stored, by an earlier build or a direct database write, leaves ACME unavailable: dcrouter builds neither SmartAcme nor SmartProxy's ACME options from it, logs an AcmePermanentFailureError (invalid-renewal-threshold) that points to Domains > Certificates > Settings and updateAcmeConfig, and certificate issuance reports acme_unavailable, while SmartProxy and the OpsServer keep running for the correction. test/test.acme-renewal-threshold.node.ts runs dcrouter's provision path against SmartAcme's loopback CA: a stored 90-day certificate with 20 days left is renewed under the default threshold and kept under a configured 10 days. It also starts dcrouter on a stored threshold of 0: SmartProxy runs without SmartAcme, the OpsServer lists the error in its logs and accepts a corrected threshold. smartacme 9.8.1 stores through @lossless.org/client 1.5.1, which the tree already carried, so the deprecated @push.rocks/smartdata 7.4.0 leaves the lockfile.
    • Defer certificate provisioning while SmartAcme is not ready, and consume @push.rocks/smartproxy 27.22.0 (from 27.21.0), whose certProvisionFunction may answer 'defer'. dcrouter answered SmartProxy's provision callback with acme_unavailable while SmartAcme was starting, retrying or out of its startup budget. SmartProxy recorded that as a failure and skipped the domain for its 30-minute cooldown, including in the sweep dcrouter runs once SmartAcme becomes ready, so after every restart the routes that needed a certificate got none until a sweep ran after the cooldown. The callback now defers whenever issuance is not ready, which also covers ACME being disabled and an account change waiting for the rebuild. SmartProxy records nothing for a deferral, and the sweep that follows SmartAcme becoming ready, after rearm() too, issues the certificate. The log line for an exhausted SmartAcme startup budget, a code comment and the readme said the callback fell back to HTTP-01; certificate provisioning is DNS-01 only, and they now say what the code does. test/test.acme-provision-cooldown.node.ts runs a real SmartProxy with dcrouter's callback and SmartAcme lifecycle against SmartAcme's loopback CA: the startup sweep is deferred without a cooldown and the domain is issued once SmartAcme is ready, and the same holds after a permanent startup failure and rearm(). It also rebuilds SmartProxy on the same DcRouter after ACME was disabled and shows that neither SmartAcme nor the provision callback remains.
    • Let an operator reprovision take effect at once after a failed attempt. A failed provisioning attempt puts the domain on SmartProxy's 30-minute provisioning cooldown, and reprovisionCertificateDomain only re-applied the routes, whose sweep skipped the cooling-down domain without a log line, so the reprovision did nothing until the cooldown ended. The handler now goes through the new DcRouter.reprovisionProxyCertificate(), which lifts the domain's cooldown with smartproxy 27.22.0's invalidateCertProvisionCooldown() before it re-applies the routes, and the sweep asks for the certificate again. A domain that was not cooling down is reprovisioned as before. The message returned while the proxy still serves an expired certificate said SmartProxy exposed no per-domain invalidation and that toggling the route off and on was the only remedy; it now says that the background provisioning sweep had not replaced the certificate when the check ran, or failed. test/test.acme-provision-cooldown.node.ts fails an attempt on a zone whose delegation is not verified yet, verifies it and reprovisions: the certificate is issued without waiting for the cooldown.
    • Correct the documented reload semantics of the ACME settings. SmartProxy and SmartAcme receive them, renewThresholdDays included, when dcrouter builds them, so a change takes effect at the next restart or SmartProxy rebuild. The config manager, the updateAcmeConfig contract and the settings dialog said the threshold applied to the next renewal check at once.
    • Store a renewed certificate's id with its material. SmartAcme now renews every certificate by storing the replacement over the due one, as forced renewals already did, and dcrouter's certificate store kept the old id on an overwrite, so the record, and a certificate exported from it, named a certificate it no longer held.
    • Keep an abandoned testing certificate job from ordering a replacement. smartacme 9.8.1 replaces an abandoned exact-identifier attempt with a new key and order on the next request once an hour has passed, where 9.7.0 answered it as pending for good. The grant reservation already kept new jobs out, but a job claimed again after its abandon, because recording the result was interrupted, requested the certificate and relied on that old answer. A job now publishes an attempt it finds abandoned as abandoned_unverified instead of requesting it. The readme and the job model state that abandonment is final at dcrouter's job layer, not in SmartAcme. test/test.testing-certificates.node.ts interrupts the recording of an abandon, lets SmartAcme's retry time pass and shows that the job claimed again stays abandoned without a request.
    • Let a stopped DcRouter, or one whose start failed, start again. start() after stop() on the same instance failed with Critical service 'OpsServer' failed to start: a TypedHandler for getAdminBootstrapStatus alredy exists!, because each OpsServer linked its routers into the DcRouter's main typedrouter and never detached them, so the new OpsServer's handlers collided with the stopped one's. OpsServer now links into that router in start() and detaches both links in stop(), also when its cleanup fails. A failed start(), such as one whose port is taken, runs the same cleanup before it rethrows its error, because the service manager never stops a service whose start failed; a failing cleanup is raised together with the start error. Either way the next OpsServer composes into a router that no longer reaches the previous one's handlers.
    • Log service events again after a DcRouter is stopped and started, and consume @push.rocks/taskbuffer 9.0.4 (from 9.0.3), which carries the root fix. taskbuffer 9.0.3's ServiceManager.stop() unsubscribed the forwarders that feed serviceSubject and start() never recreated them, and dcrouter subscribed to serviceSubject once in its constructor and ended that subscription in stop(), so the ops log received no service event after the first restart. DcRouter now subscribes in start() and unsubscribes in stop(), and also when start() fails. test/test.opsserver-restart.node.ts shows that the second run's Service 'OpsServer': started event reaches the ops log; the test fails with taskbuffer 9.0.3 and passes with 9.0.4.

    Maintenance

    • Build, test and watch tooling on its current line: @git.zone/tsbuild 5.0.0 (from 4.5.1), @git.zone/tsrun 3.0.0 (from 2.0.6), @git.zone/tstest 6.2.0 (from 6.1.5) and @git.zone/tswatch 4.0.1 (from 3.3.5); tsbundle 2.14.0, tsdocker 3.8.1 and tsdeno 1.8.0 were already current, and pnpm 12.4.2 and @types/node 26.6.1 stay until their successors are seven days old. No source folder carries its own tsconfig.json or tspublish.json compiler options, so tsbuild 5's folder configuration needs no migration. Under tsrun 3 the cli.ts.js development entry gives the script Node's standard process.argv, which runCli() already reads with process.argv.slice(2); tsrun 2 passed the user arguments alone, so that slice dropped them, and node cli.ts.js --version now prints the version. The dcrouter-dev watcher in .smartconfig.json keeps its shape under tswatch 4. tswatch 4 brings @api.global/typedserver 13 and @api.global/typedsocket 8.5.0 into the dev tooling only; dcrouter's runtime dependencies are unchanged in the lockfile.
    Downloads
  • DcRouter v32.3.0
    Release / build-and-release (push) Successful in 18m12s
    Stable

    jkunz released this 2026-09-23 03:31:42 +00:00 | 34 commits to main since this release

    2026-09-23 - 32.3.0

    Features

    • @serve.zone/dcrouter-apiclient now publishes in the same gitzone release as @serve.zone/dcrouter and the Docker images, at the same version, so the client package no longer trails the runtime. gitzone 7 publishes the named tspublish module before the root package, records every package on every registry in the release journal, and promotes the images only once both packages are verified; a resumed release publishes only what is still missing. The publish:packages script and the @git.zone/tspublish dev dependency that existed for it are gone, because the release is now the only publisher and gitzone refuses to release beside a script that runs the standalone one. @serve.zone/dcrouter-apiclient has no release for 32.2.0 and earlier, so a consumer pinned to one of those versions keeps importing the client from @serve.zone/dcrouter/apiclient.

    Maintenance

    • Test tooling on its current line: @git.zone/tstest 6.1.5 and @types/node 26.6.1; pnpm, tsbuild, tsbundle and tsdocker were already current. tstest 6.1.5 moves its createSmartmongo() helper from @lossless.org/nosqldb 8.0.0 to 10.5.0; dcrouter's suites do not start that helper, so only the lockfile's single nosqldb copy changes. The lockfile keeps one copy of each tool and drops the older @push.rocks/smartcli 4.3.0, @push.rocks/smartconfig 6.2.0, @push.rocks/smartpuppeteer 2.4.0 and @types/node 26.4.1 copies in favour of the versions already beside them.
    Downloads
  • DcRouter v32.2.0
    Release / build-and-release (push) Successful in 18m43s
    Stable

    jkunz released this 2026-09-23 00:26:48 +00:00 | 37 commits to main since this release

    2026-09-23 - 32.2.0

    Features

    • Add @serve.zone/dcrouter-apiclient, a package that carries the client half of dcrouter, the API client and the shared contracts, without the server. This release defines the package; publishing it is a separate step, described in the last point below. ts_apiclient/tspublish.json now names the package and owns ts_interfaces through folders, exporting the client at . and the contracts at ./interfaces; ts_interfaces keeps its build order and is still compiled first. A consumer that only calls the OpsServer API — gitzone testing, custom CLIs, integration suites, anything that speaks /typedrequest — declares 8 dependencies instead of the runtime package's 48, and resolves a production closure of 184 packages instead of 579. Gone from that closure: the native argon2 build, mongodb, @lossless.org/client, @design.estate/dees-catalog, @push.rocks/smartdata and @push.rocks/smartbucket. @serve.zone/dcrouter keeps its ./interfaces and ./apiclient subpath exports unchanged, so existing consumers migrate when it suits them.
      • The two folders ship as one package rather than two, because @git.zone/tspublish requires sibling packages to import each other through their published names. That bare specifier would land in dist_ts_apiclient/*.d.ts, which the runtime package also ships, and would force @serve.zone/dcrouter to declare a same-version dependency on a package cut from the same release. Owning ts_interfaces through folders keeps the existing relative import valid inside the published tarball and leaves the runtime package's exports self-contained.
      • What the new package does not shed: @push.rocks/smartproxy, @push.rocks/smartmta and @push.rocks/smartnetwork stay runtime dependencies, because contract members are typed against them (IRouteConfig, IUnifiedEmailServerOptions, IIpIntelligenceResult, and IRouteChallengeConfig from @push.rocks/smartchallenge; the gateway contracts come from @serve.zone/interfaces). A type-only import still has to resolve for a consumer's type check, so the three Rust payloads — each tarball carrying both the amd64 and the arm64 binary — are installed exactly as before, and so is @push.rocks/smartlog 3.2.2, reachable through @api.global/typedsocket, @push.rocks/smartmta, @push.rocks/smartproxy and @serve.zone/interfaces alike. The 1.1 MB figure this package is worth quoting is its own packed payload, not its install footprint.
      • Because @push.rocks/smartmta declares os: ["linux"] and cpu: ["x64", "arm64"], the new package installs on Linux x64 and arm64 only, exactly like @serve.zone/dcrouter itself. The readmes say so at the install step. This is not a dcrouter-side choice and no dcrouter change lifts it: it goes away when @push.rocks/smartproxy, @push.rocks/smartmta and @push.rocks/smartnetwork move their Rust payloads into per-platform optional packages.
      • pnpm run publish:packages runs tspublish over the repository after a release and publishes the package at the released version; @git.zone/tspublish is a dev dependency so the descriptor can also be planned and prepared locally. gitzone release keeps publishing the runtime package and qualifying the Docker images unchanged — it cannot carry a tspublish module and a Docker target in the same release.

    Maintenance

    • Build, test and release tooling on its current line: pnpm 12.4.2, @git.zone/tsbuild 4.5.1, @git.zone/tsbundle 2.14.0, @git.zone/tsdocker 3.8.1 and @git.zone/tstest 6.1.4. tsbundle 2.14.0 bundles the web UI with dcrouter's own tsconfig instead of the packaged one; the lockfile keeps one copy of each tool.
    • Git and the Docker build context ignore .env and .env.* files, so a local environment file cannot reach a commit or an image.
    Downloads