Repository navigation
refactor: share auth-plugin construction and wire the Worker's KV stores - #20
Merged
Merged
Conversation
buildAuthPlugins and flattenMethods lived inside server.ts, so the Cloudflare worker entry carried its own divergent copy of the same switch -- one that had no `agent_allow_list` or `delegated_verification` cases at all, and picked up new auth methods only when someone remembered to update both. Extracted to src/auth-plugins.ts and taken by both entry points, with the deps passed as an options object rather than four positional arguments. That extraction is what lets the worker entry wire its stores, since the old local copy had nowhere to accept them. It now constructs all three against the SESSIONS binding -- KvAllowedAgentStore, KvNetworkStore, and LinkerRegistrationStore -- gated on the same config as the Node entry point, so the allowed-agent, network, and linker admin routes work on Workers with no manual wiring. The linker store also feeds KvUrlProvider, merging dynamically registered linkers into URL provisioning alongside the static ones. Note that deploy/ is outside tsconfig's `include` and no test covers worker-entry.ts, so this file is checked by neither `npm run typecheck` nor `npm test`; the release workflow bundles it with esbuild, which does not typecheck.
The docs still said dynamic registration of allowed agents and networks needed manual worker wiring and was Node-only out of the box; that stopped being true once the bundled worker entry started constructing the KV-backed stores itself. Also records the gap that remains: Node has no KV-shaped store for dynamic *linker* registration, so running that on Node means embedding the service as a library and supplying a linkerRegistrationStore.
Adds a "Client Integration" section covering the decision a client makes on startup -- which endpoint to call given whether an agent key exists and whether the hApp is installed -- why reconnect goes first (both orders are safe, so it is about what each request needs from the caller: reconnect a key and a signature, join possibly claims and a user prompt), and the requirement to record the agent-key-to-hApp association locally before calling /v1/join. The Overview's flow summary gains the matching recovery stanza. It lands as section 4, so the sections after it are renumbered: Error Response Format 4->5, CORS and Rate Limiting 5->6, Security Considerations 6->7, Authentication Methods Reference 7->8, Example Flows 8->9 (and its 8.x subsections to 9.x), TypeScript Type Definitions 9->10. Two cross-references name a moved section and are updated: auth methods in the /v1/info field table, and the src/types.ts header comment pointing at the type definitions. References to 3.x subsections are unaffected. Also adds example flow 9.10, recovery after an interrupted install, and corrects flow 9.5's reconnect response, which named a `linker_urls_expire_at` field the service does not return -- expiry is per linker_urls entry.
Security Considerations now states what the service does and does not do: admission control, not fork prevention. A key holder can copy the conductor directory and fork without ever calling the service; that is Holochain's problem, handled by validation and warrants. Records why the 409 stays as it is -- /v1/join has no proof of key possession, so an idempotent join would turn a public agent key into a provisioning credential and skip auth evaluation. Adds the reconnect replay window: the signed payload is the timestamp alone within a configurable tolerance (default 300s) and ready sessions do not expire, so an observed request replayed inside the window returns the session token. Notes the available mitigations and that signing over the agent key plus a nonce is the durable fix. Also corrects the session-expiry bullet, which claimed 1 hour pending / 24 hours ready; the stores never expire ready sessions and pending sessions use session.pending_ttl_seconds (default 24 hours).
Covers JoiningClient: constructing one, choosing a network, driving the join/verify/provision flow, and the crash-recovery path via reconnect. Both construction paths get their own example rather than one worked example plus a prose aside, since which one applies is decided by whether the app domain publishes a well-known document, not by preference -- and the choice determines whether join() can route to a network on its own. The three forms of join()'s network argument are likewise shown as three calls instead of described as one three-way parameter. Provision handling branches on what is present instead of assuming a shape. Every field is optional and which ones arrive is a property of the deployment: a membrane-proof-only or gateway-only service returns no linker_urls at all, and /v1/info omits linker_info to match, so an absent linker is the normal case for those deployments rather than an error.
ThetaSinner
approved these changes
Aug 20, 2026
docs: client integration guide, admission-control scope, and README client library
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.
Split out of the multi-network PR: this is the Cloudflare Worker half, which touches no joining logic and reviews on its own.
The Worker entry point had drifted from the Node one.
buildAuthPluginsandflattenMethodslived insidesrc/server.ts, sodeploy/cloudflare/worker-entry.tscarried a hand-copied version of the same switch — one that had silently droppedagent_allow_listanddelegated_verificationentirely (hit in production by the unyt fork). Both entry points now call one sharedsrc/auth-plugins.ts, with deps passed as an options object rather than four positional arguments.That extraction is the prerequisite for the rest: the Worker's local copy had nowhere to accept a store, so it could not wire one. It now constructs all three against the
SESSIONSbinding —KvAllowedAgentStore,KvNetworkStore, andLinkerRegistrationStore— gated on exactly the same config as the Node entry point. Every dynamic-registration admin surface works on Workers with no manual wiring. The linker store also feedsKvUrlProvider, so dynamically registered linkers merge into URL provisioning alongside the static ones.AuthMethodgets its own case, so TypeScript narrows thedefaultbranch to exactly the customx-${string}member. Adding a method to the union insrc/types.tswithout a case for it now fails to compile — this is the class of bug that let the Worker skip whole auth methods unnoticed.DEPLOYMENT.mdandJOINING_SERVICE_API.mdno longer claim dynamic registration needs manual worker wiring or is Node-only.Breaking / operational notes:
delegated_verificationlisted inauth_methodswithout its config block, oremail_codewithout a transport. Previously the Worker's local switch had no case for these and skipped them; a deployment that was quietly running without an auth method it thought was enabled will now fail to start. This is the intended behavior (it matches Node), but check Worker configs before deploying.LinkerRegistrationStoreis built against a KV-namespace-shaped store and no Node (sqlite/memory) implementation of that shape ships. Running it on Node means importingcreateAppand supplying your ownlinkerRegistrationStore. Documented, not fixed here.Stacks on the multi-network PR — merge that first.
Commits (2)
refactor: share auth-plugin construction and wire the Worker's KV storessrc/auth-plugins.ts, both entry points, three KV storesdocs: dynamic registration on Workers and NodeDiff vs the multi-network tip: 7 files, +270/-184.