Repository navigation
Keep nodes added by address persistent with explicit PAIR or engine intent - #45
Merged
Merged
Conversation
Keep broker-owned manual addresses first-class without allowing a PAIR entry to downgrade after worker replay. Persist kind, conservatively migrate historical entries to PAIR, and reserve the labelled manual bypass for explicitly chosen engines. Retain proven associations only for lifecycle cleanup, and forget PAIR addresses after successful unpair for every client. Add matching TUI choices and regression coverage. Broker persistence equivalent to NVIDIA/Personal-AI-Router@aed93b6 (NVIDIA#60) is already present in the fork; retain that single owner rather than re-porting it. Adapt the successful-unpair cleanup from NVIDIA/Personal-AI-Router@692950c (NVIDIA#172) at the broker boundary, and adapt the normative specification from NVIDIA/Personal-AI-Router@b3b25ae (NVIDIA#128) to the pinned-mTLS trust model. Co-authored-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Co-authored-by: Terve <ntervalon@nvidia.com> Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
Make Add node save explicit PAIR or engine-only intent through the broker before starting PIN pairing. Keep unsuccessful pairing as an unverified address, expose manual entry listing, retry and removal under Cluster settings, and label the engine-only bypass. Add typed bridge channels and interaction tests without changing node-card verification or discovery keying. Remove the old eager, ignored manual removal from the desktop membership handler: successful-only unpair cleanup now belongs to the shared broker transaction. Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
Keep probe state commits and node notifications in the same critical section as replacement and removal. Publish an empty replacement status immediately so a changed kind withdraws its prior route before the next network probe. Late initial-discovery callbacks also check the exact tracked entry under the publication lock. An ordered interleaving regression pauses an engine probe at its publication boundary, replaces it with PAIR intent, and detects the obsolete engine update restoring the bypass. A separate regression requires withdrawal before a blocked replacement probe completes. Both failed before this fix; the race suite and fifty repeated ordered runs pass. Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
Emit a broker-internal completion association only after the cluster manager commits authenticated pairing. Retain the original dial hostname, not the numeric source address stored in the roster, and persist matching PAIR aliases for cleanup before any node-info inventory proof. The association never enters renderer state or grants identity, inventory, telemetry, or routing authority. Include completion associations received while unpair is in flight, so successful removal cannot leave a hostname for replay. Producer and broker regressions failed before the fix; they now cover hostname versus numeric roster address, broker restart before proof, independent engine entries, abandoned pairing, and an ordered in-flight completion. Add the internal contract exception, regenerate service documentation, and bump cluster-manager to 2.2.0 for its additive notification. Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
Publish authenticated pairing completion inside the roster commit/removal transaction, and include its cluster and admission epoch. The broker rejects obsolete or removed admissions before writing cleanup associations. An already in-flight unpair may retain at most sixteen transient original-address hints so it can forget an unassociated hostname even if the roster file was removed before notification consumption; hints expire on success or failure and never provide routing or identity authority. The notification is fire-and-forget: publishing under rosterMu never waits for an acknowledgement or a broker request response. The independent broker worker read pump consumes it while the request caller may be relaying nodes:remove. Its consumer does no worker round trip and never waits on manualMutationMu, avoiding a request/publication lock cycle. The ordered producer/removal regression and obsolete-completion regression both failed before this fix. Current race suites, three-platform vet, desktop gates and contract checks pass. Tests also cover a removed roster during an in-flight unpair, successful-removal admission guards, and bounded hint expiry. Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
Match an associated manual entry only against its current persisted peer UUID. Use cached identity, address and transient-hint fallbacks only for entries with no association. Cached inventory and ordinary re-adds can seed missing metadata but cannot overwrite a current pairing association. Recheck ownership under the persistence lock when deleting each alias, not only when collecting pre/post-removal snapshots, so authenticated reassociation while the removal RPC is in flight preserves the new peer entry and its live worker record. Regressions for A-to-B hostname reassociation, reused numeric addresses, stale cached proof and re-add, and in-flight reassociation all failed before this fix and now pass. Seven Go race suites, native/Linux/Windows vet, desktop gates and contract checks pass. Signed-off-by: Jared Ping <13906915+JaredP94@users.noreply.github.com>
JaredP94
force-pushed
the
feat/manual-nodes-first-class
branch
from
November 3, 2026 15:51
98e0e67 to
cd76f56
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Make addresses added across Tailscale, WireGuard or other routed links persistent first-class entries without weakening #43's pinned-mTLS trust model.
Users explicitly choose PAIR machine (save the address, then PIN-pair) or Engine only (a labelled manual bypass that trusts no PAIR claims). Failed pairing keeps an unverified entry with retry/removal controls. PAIR entries never become bare-engine routes after worker/broker restart or loss of node-info. Unverified PAIR entries are
preview:<sha>; engine bypasses remainmanual:<hash>.Upstream context: NVIDIA#10 and NVIDIA#92. Broker persistence equivalent to NVIDIA/Personal-AI-Router@aed93b6 (NVIDIA#60) was already present on the fork and was retained, not duplicated. Adapted NVIDIA/Personal-AI-Router@692950c (NVIDIA#172) successful-unpair cleanup at the shared broker boundary, and NVIDIA/Personal-AI-Router@b3b25ae (NVIDIA#128) normative documentation to this fork. Upstream contributors are credited in the signed-off commit.
Scope
configs/manual-nodes.json; no desktop store or second replay path. Persist/replaykind: pair | engineand retain previously proven associations solely for offline removal, never routing authority.pair. Classification cannot safely infer prior consent to an engine bypass. Former bare engines stop direct routing until removed/re-added as Engine only. Unknown kinds fail closed.host:porterrors. Existing authenticated overlay routing already handled manual addresses after Require verified peer inventory for service decisions #43; no proxy runtime change was needed. Added hostname mTLS inventory and three proxy transport-selection tests.aPAIR,eengine,pretry pairing). Shared desktop types are additive; node-list keying and card verification remain owned by Keep desktop peer previews separate from verified machines #44.v0.8.0: retain existing unreleased minor bumps for manual-nodes (1.3.0) and broker (2.5.0); bump TUI to1.3.0for its additive manual-kind surface and cluster-manager to2.2.0for the broker-internal authenticated pairing completion notification. Proxy changes are tests only; no proxy version changes. No release/changelog edit.Validation
Local isolated worktree
/tmp/pair-work/manual-nodes, ownnpm ci --no-audit --no-fund; macOS arm64, Go 1.27.3, Node 26.7.0.gofmt -l,go vet ./...,go test -count=1 ./...,GOOS=linux go vet ./...,GOOS=windows go vet ./...passed. Worker/broker race suites passed as well.origin/mainatb7be184); retainednodeRecordKey,nodeVerificationLabel,canPairByAddressand the discovered-list modal regression. Post-rebase desktop typecheck, lint with--max-warnings=0, 858 tests across 87 files, dead-code check, regenerated service contracts and contract check passed.TestLiveOMLXCacheAffinityConversationskipped and remains untested; no live engine test was enabled. No other native unit skips.git diff --checkpassed.Independent-review follow-ups
3489d28serializes node-status/telemetry publication with replacement/removal, including exact tracked-entry checks for initial discovery. Kind replacement publishes an empty initial status immediately.TestEnginePublicationCannotFollowPairReplacementforces the reported old-engine/new-PAIR interleaving without sleeps;TestKindReplacementWithdrawsBeforeNewProberequires immediate withdrawal. Both failed before the fix, pass under the race detector, and passed 50 repeated ordered runs.052299femits broker-internalcluster:pairing-completedonly after authenticated pairing commit, carrying the original dial hostname rather than the observed numeric roster address. The broker persists existing PAIR aliases' cleanup-only association before later cluster-manager responses, including completion while unpair is in flight. It never marks an entry verified or supplies service authority.TestPairingCompletionPublishesOriginalHostnameForCleanupandTestPairingCompletionForgetsHostnameBeforeInventoryProoffailed before the fix; the latter includes broker restart before any node-info proof.TestPairingCompletionDuringUnpairForgetsHostnamecovers the ordered in-flight notification.go test -race -count=1 ./...and native/Linux/Windows vet passed in all seven affected Go modules, now including cluster-manager. Desktop gates still pass (858 tests); contracts regenerated with an explicit internal-notification exception. SPDX: 1,299 checked, 0 missing.TestMacmonSupervisorBackoffDoublesCapsAndResetstiming-cap assertion. Run 41645196846 at3489d28subsequently passed all six jobs. Neither is claimed as final-head certification.Final lifecycle ordering and reassignment corrections
7d4039c: completion publication now occurs under the producer's roster commit/removal lock, with cluster/admission binding. It is fire-and-forget and never waits for a broker acknowledgement or request reply; the independent reader consumer does no worker round trip or mutation-lock wait. The broker rejects obsolete/removed completions. At most 16 transient address hints support an already in-flight unpair when the roster file has already lost the peer; they expire on every exit and grant no identity/routing or persistence authority.TestPairingCompletionPublicationOrdersConcurrentRemovalandTestObsoletePairingCompletionCannotAssociateManualEntryfailed before this fix. Additional tests cover removed-admission guards and hint expiry/bounds.98e0e67: a persisted peer association is authoritative over cached identities, reused addresses and hints. Cached inventory/re-add cannot overwrite it. Deletion rechecks association ownership atomically with the durable write, protecting a reassociation that arrives during removal.TestCurrentManualAssociationSurvivesStaleObservationAndReadd,TestUnpairAssociationOverridesCachedIdentityAndReusedAddress(hostname and numeric address), andTestUnpairRechecksAssociationChangedDuringRequestall failed before this fix and now pass.go test -race -count=1 ./..., native/Linux/Windows vet. Desktop gates passed again (858 tests), contracts regenerated/checked; SPDX: 1,303 checked, 0 missing. No cross-process suite was run locally.Final-head CI (98e0e67, run 41664943412) and Linux artifact skip audit are pending.
Risk
Migration intentionally stops historical bare-engine routing until the user explicitly chooses Engine only. Pairing failures leave an unverified saved address, not an engine fallback. No opt-in unclustered PAIR route exists. Cleanup failure after successful unpair is reported explicitly; membership may already be removed, and the manual entry can be forgotten separately after resolving the error.
DNS names are not frozen to resolved addresses; live pinned connections can be reused, and fresh connections re-resolve. Local tests and CI are not a live overlay deployment or packaged-artifact certification. Live multi-machine operation, Windows runtime and desktop launch remain unverified.
Checklist
git commit -s), certifying the Developer Certificate of Origin.services/versions.json, and described user-visible changes above so they reach the release notes.