Repository navigation
feat(accounts): a module_actor binding names the exchanging module as the child's actor - #1215
Merged
Merged
Conversation
… the child's actor An installed operation binding marked `delegation_mode: module_actor` exchanges a person's parent through Core's Child with the authenticated calling module as its one actor hop, for any invoke scope and for a module or `solution:` audience. It reuses the installed content-read child: the binding id is namespaced (`installed-actor/`), commits to both installations' lifetimes, resolves for Core's seal check and is confirmed by the existing module-hop revision check. The hop is journaled. Ordinary bindings, the chain-preserving exchange and StartTask are unchanged. Part of #1200.
… reaches named sources A module_actor binding addressed to a module now names only kinds that module serves, the check content_read already makes. It may also address the host's own source operations: every scope names datasource.sources with explicit source ids and carries only invoke and read. The source surface then takes the child as the person's call with the module acting, for those sources only. The existing callee path (requireCurrentAuthority, then the installed binding) refuses it once the module's installation or the binding changes, or the person loses access to the source. Part of #1200.
lukazhupa
force-pushed
the
feat/exchange-module-hop
branch
from
October 22, 2026 16:13
17e1a05 to
e86bbc8
Compare
lukazhupa
marked this pull request as ready for review
October 22, 2026 16:14
This was referenced Oct 22, 2026
Merged
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.
Part of #1200: the explicit first-module-hop mode on the operation exchange, for a module, a
solution:audience or the host's own source operations, plus the binding resolution Core's seal check needs for it.It builds on the installed-binding child #1198 added.
What changes
An installed operation binding may set
"delegation_mode": "module_actor"in the module envelope. A module that exchanges a person's parent through that binding (ExchangeDelegatedOperationAudience) gets Core's Child with one actor hop:moduleCaller), never a value the request supplies.installed-actor/…, committing to the caller's installation, presence and envelope facts, and to the audience's installation where it has one.The audience must be one of three:
content_readmakes.solution:audience. It's held by the organization's installation of that solution, so an uninstall or a reinstall retires issued children.saas-datasource). Every scope namesdatasource.sourceswith explicit source ids and carries onlyinvokeandread. The source surface takes the child as the person's call with the module acting, for those sources only.Callee path (source operations).
authenticateSourceOperationContextverifies the child, which runs Core's seal check against the installed binding. It then goes throughrequireCurrentAuthority→requireInstalledBinding, which re-resolves the binding, confirms the hop and rechecks the person's own authority. After a successful call, the same child is refused when:MintSourceOperationContextis unchanged: a connector-owned delegation keeps its meaning, and this path never borrows it.Opt-in by installed policy: the mode belongs to the binding, never the request. These are unchanged:
It adds no request field, registry, permission vocabulary or store.
Reuse
InstalledContentReadBindingdelegates toInstalledBinding(mode), and the exchange isexchangeInstalledBindingfor both modes.Childand its seal rule: the hop's binding resolves throughWorkContextOperationBinding.ConfirmModuleHopBindingresolvesinstalled-actor/ids the same way it resolvesinstalled-read/ones.requireCurrentAuthorityroute torequireInstalledBinding.journalActorHop,attenuateEffective,WorkContextAudienceInstallation.Deliberately not reused:
MintSourceOperationContext: its owner is the connector, not the asking person.Classification
Owner fix. The host issues the first module hop at the exchange it owns, through its existing binding and seal machinery.
For the owner to confirm
solution:audience isn't held to served kinds, because the host records none for a solution target. It's held to the organization's installation of that solution.Tests
From the accounts code root,
GOWORK=off go test ./pkg/adapters ./pkg/businesspasses. Inmodule/tools,TestModuleCapabilityEntryPointsHoldAuthorityThroughUsepasses.go vet, gofmt, goimports andscripts/check-boundaries.share clean on every file this change touches.New or extended tests:
TestModuleActorExchangeNamesTheExchangingModule: the hop equals the module Work Context's owner, scopes are attenuated, and owner, tenant, task and expiry come from the parent. The hop is journaled, and the revision check accepts the child when it's sentoperation_binding_id.TestModuleActorExchangeRefusals: each of these is refused:TestModuleActorSolutionAudienceFollowsItsInstallation: reinstalling retires an issued child, and once uninstalled nothing is exchanged.TestModuleActorSourceCallIsThePersonsWithTheModuleActing: for invoke and lookup, the child is the person's with the module as its one hop, narrowed to one source. The source surface accepts it for that source and refuses it for another.TestModuleActorSourceChildRetiresWithItsStanding: after an accepted call, the same child is refused when the installation changes, when the binding narrows, and when the person loses access to the source.TestModuleActorChildIsTerminal,TestInstalledModuleActorModeShapeandTestSourceOperationActorScopeNamesItsSources.Not run: the DB-backed
pkg/infrasuites, andcodefly ci run.Out of scope (the rest of #1200)
AuditSourceOperationstill records the owner as actor typeuser. Recording the module actor touches the engine's dispatch path and fix(accounts): audit callable-source scope refusals #1123's open audit work.invokealso reaches that source's reconcile and prune operations. Per source is the floor this surface checks today.IssuedWorkContext.current_actor_principal_idand sends the child's binding id asoperation_binding_idon its revision check.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ehgefxu5moNkjuHfTJofXM