Skip to content

ApiToken issuance: confirm node readable before returning (fix MCP reconnect reject) - #624

Merged
1 commit merged into
mainfrom
fix/apitoken-issue-readback
Jul 24, 2026
Merged

1 commit merged into
mainfrom
fix/apitoken-issue-readback

Conversation

@rbuergi

@rbuergi rbuergi commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Problem (the next layer of the MCP-connect failure, observed live)

After #620 fixed the OAuth code exchange, the /token flow now succeeds and issues an mw_ API token (live logs: Node created at rbuergi/ApiToken/... + global index). But the MCP client's immediate reconnect was rejected — user report: "Got new credentials, but memex rejected them on reconnect."

Same create→routable lag, one layer up: ApiTokenService.ValidateToken reads the just-created token/index nodes via the one-shot hub.GetMeshNode(path), which hits [ROUTE] NotFound during the window before the node is routable on the validating replica (issuance and reconnect often land on different replicas).

Fix

Confirm-readable at issuance, not on the hot validation path. After creating the token node + global index, CreateToken reads BOTH back through the resilient GetMeshNodeStream poll (self-paced Concat, swallows the transient "No node found", 10s bound, best-effort → returns the token anyway on timeout) BEFORE returning the raw token. This activates each node's owning per-node hub from Postgres, so the token is validatable on any replica by the time /token returns. ValidateToken keeps the fast one-shot read — only the one-time login pays the read-back.

Verification

No What's New entry — completes the MCP-connect fix chain; user-facing effect covered by #609's entry.

🤖 Generated with Claude Code

…reconnect reject)

After the OAuth code-exchange fix (#620) the /token exchange now succeeds and
issues an mw_ API token — but the client's immediate reconnect was rejected
("Got new credentials, but memex rejected them on reconnect"). Same create→
routable lag, one layer up: ValidateToken reads the just-created token/index
nodes via the one-shot hub.GetMeshNode, which hits [ROUTE] NotFound during the
window before the node is routable on the validating replica.

Fix at issuance, not on the hot validation path: after creating the token node
and its global index, ApiTokenService.CreateToken now reads BOTH back through the
resilient GetMeshNodeStream poll (self-paced Concat, swallows the transient
"No node found", 10s bound, best-effort) BEFORE returning the raw token. This
activates each node's owning per-node hub from Postgres, so by the time /token
returns the token is validatable on any replica — while ValidateToken keeps the
fast one-shot read (only issuance, a one-time login, pays the read-back).

Verified: MeshWeaver.Auth.Test ApiToken suite 50/50 (real run, Release). The
monolith tests confirm no regression; the multi-silo reconnect is verified live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 23, 2026 22:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ensures newly issued API tokens are immediately readable across replicas in a multi-silo portal by confirming both the per-user token node and the global ApiToken/{hashPrefix} index node are readable (via GetMeshNodeStream polling) before returning the raw token, reducing [ROUTE] NotFound rejections on the MCP client’s immediate reconnect.

Changes:

  • Added a best-effort ConfirmReadable(path) helper that polls GetMeshNodeStream(path) with a bounded retry loop under System identity.
  • Updated CreateToken to confirm readability of both the index node and token node before returning TokenCreationResult.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +281 to +282
/// than failing issuance — the token IS created; worst case validation resolves it a
/// beat later. System identity: the ApiToken/index nodes are System-owned.
Comment on lines +289 to +292
.Where(n => n is not null && n.Content is not null)
.Select(n => (MeshNode?)n)
.Catch<MeshNode?, Exception>(_ => Observable.Empty<MeshNode?>())
.Concat(Observable.Defer(Attempt).DelaySubscription(TimeSpan.FromMilliseconds(50)));
@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

 11 files   -  1   11 suites   - 1   3m 9s ⏱️ - 1m 27s
906 tests  - 59  902 ✅  - 59  4 💤 ±0  0 ❌ ±0 
968 runs   - 59  964 ✅  - 59  4 💤 ±0  0 ❌ ±0 

Results for commit 9354353. ± Comparison against base commit 10b2d13.

This pull request removes 59 tests.
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ ActivityLogs_ShouldBeQueryableViaMeshQuery
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AllNodeTypes_ShouldExist
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AmericasIns_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AmountTypes_ShouldLoadFromMeshNodes
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_EmbeddedCharts_ShouldRenderViaPathResolution
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_Overview_ShouldContainLayoutAreaReferences
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AsiaRe_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ BusinessUnits_ShouldExistWithProperties
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ Countries_ShouldLoadFromMeshNodes
…

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

934 tests  ±0   928 ✅ ±0   4m 51s ⏱️ -1s
  5 suites ±0     6 💤 ±0 
  5 files   ±0     0 ❌ ±0 

Results for commit 9354353. ± Comparison against base commit 10b2d13.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 946 tests  ±0   1 758 ✅ ±0   5m 0s ⏱️ +48s
   12 suites ±0     188 💤 ±0 
   12 files   ±0       0 ❌ ±0 

Results for commit 9354353. ± Comparison against base commit 10b2d13.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 111 tests  ±0   1 008 ✅ ±0   5m 20s ⏱️ -18s
   10 suites ±0     103 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit 9354353. ± Comparison against base commit 10b2d13.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 112 tests  ±0   1 111 ✅ ±0   5m 57s ⏱️ +2s
   12 suites ±0       1 💤 ±0 
   12 files   ±0       0 ❌ ±0 

Results for commit 9354353. ± Comparison against base commit 10b2d13.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 165 tests  ±0   1 163 ✅  - 1   7m 2s ⏱️ +29s
   10 suites ±0       1 💤 ±0 
   10 files   ±0       1 ❌ +1 

For more details on these failures, see this check.

Results for commit 9354353. ± Comparison against base commit 10b2d13.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

   60 files   -  1     60 suites   - 1   31m 21s ⏱️ -27s
7 174 tests  - 59  6 870 ✅  - 60  303 💤 ±0  1 ❌ +1 
7 236 runs   - 59  6 932 ✅  - 60  303 💤 ±0  1 ❌ +1 

For more details on these failures, see this check.

Results for commit 9354353. ± Comparison against base commit 10b2d13.

This pull request removes 59 tests.
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ ActivityLogs_ShouldBeQueryableViaMeshQuery
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AllNodeTypes_ShouldExist
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AmericasIns_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AmountTypes_ShouldLoadFromMeshNodes
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_EmbeddedCharts_ShouldRenderViaPathResolution
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_Overview_ShouldContainLayoutAreaReferences
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AnnualReport_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ AsiaRe_Overview_ShouldRender
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ BusinessUnits_ShouldExistWithProperties
MeshWeaver.FutuRe.Test.FutuReAnalysisTest ‑ Countries_ShouldLoadFromMeshNodes
…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants