e2e: which process a call runs on, counted rather than inferred - #268
Merged
Conversation
Derived instances had no end-to-end coverage for the reason they are hard to test: everything a client can observe is designed to be identical whether or not deriving happened. Exposed names, scope keys and audit records all come from the base server's id, so a test from the outside has no signal unless it counts processes. These cases count them, and the arithmetic is stated where it is asserted — the base is dialled at startup and a derived instance on first use, so one spawn after a call means the base served it and two mean it did not. The client can now report roots. That is the input derive: root keys on, and the two cases around it are a pair by design: same entry, same command, same call, and the only difference is whether the client answered roots/list with anything. The rootless one covers the documented fallback — an empty key means the base instance, which is safe because the base spec is what the operator configured — and it needs no new capability, because the default e2e client is already that client. The session case additionally asserts what must NOT change: the exposed name, and that a second call reuses the instance rather than dialling again, since a pool that did not hold it would cost a process per call. The last case is the two features meeting. execTool dials inside the call closure, after both gates, because "a call the scope gate is about to deny must not cause either" — a denied call that still dialled would leave a refusal with a side effect, and a process an attacker can spend by making calls they are not allowed to make. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Derived instances decide which process an allowed call runs on. They are a
connection-plane feature and nothing else: a derived instance shares its base
server's id, so exposed names, scope keys and audit records are identical, and
deriving never adds, hides or renames a tool.
That is exactly why it had no end-to-end coverage — there is nothing to see.
Every observation a client can make is designed to be the same whether or not
deriving happened, so a test written from the outside has no signal unless it
counts processes.
The arithmetic is what makes each case falsifiable: the base is dialled at
startup, a derived instance on first use. One spawn after a call means the
call ran on the base; two means it ran somewhere of its own.
The client can now report roots, which is the input
derive: rootkeys on —and the rootless/rooted pair differs by nothing else, so it is the pair rather
than either case that carries the result.