You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Foundation for the micro/xyz services. Partially landed in 1f6289f5.
The problem
Account identity travelled as an account_idrequest argument, so it was only as trustworthy as the caller. Nothing in the call path enforced it — it was enforced by a wrapper at each boundary where untrusted input entered, and there were two independent implementations:
apps/sdk_service.go — the app SDK over HTTP
agent/native.go:injectAccount — the agent's tool calls, guarding against prompt injection in tool content
Both correct. That was the point: the same rule implemented twice, in two packages, because the argument guaranteed nothing on its own. A third caller — a service calling another service — would have needed a third copy, and the failure mode of forgetting is silent cross-account access to mail, index, images, events.
There was no live hole: every other service.Call site is first-party Mu code building typed requests.
Done (1f6289f5)
service.WithAccount(ctx, id) / service.AccountFrom(ctx) — identity on go-micro call metadata. Verified to propagate over the in-process transport before anything was built on it. An empty id clears inherited identity, so a guest request cannot borrow the previous caller's account.
CallDynamic is the single enforcement point. It discards args["account_id"] and re-stamps from context, so no dynamic caller can scope a call to someone else by naming them.
The SDK boundary drops its own scrub and just sets the context.
Tests cover: forged account_id overwritten, guest cannot claim an account, identity reaches the handler when the caller passes nothing.
The agent still sets the request field as well as the context, deliberately — its tool calls are dispatched by go-micro straight to the service rather than through CallDynamic, so the field is what today's handlers read.
Remaining
Migrate the account-scoped handlers (mail, index, images, events) to read service.AccountFrom(ctx) instead of the request field.
Once migrated, stop setting the field in injectAccount and drop AccountID from those request structs, so the value has one source.
Account-scoped storage for services, built on the context identity. The go-micro store Mu wires is flat (internal/service/service.go:95, store.NewFileStore into $HOME/.mu/store) — no namespace, no owner, no encryption, unlike internal/userdb. This is what the personal services need (reminders, ledger, notes, habits, contacts) and it must be settled before the service contract in micro/xyzAdd full markets page at /markets with extended market data and persistent caching #7, or shipped services need their data migrated.
Foundation for the
micro/xyzservices. Partially landed in1f6289f5.The problem
Account identity travelled as an
account_idrequest argument, so it was only as trustworthy as the caller. Nothing in the call path enforced it — it was enforced by a wrapper at each boundary where untrusted input entered, and there were two independent implementations:apps/sdk_service.go— the app SDK over HTTPagent/native.go:injectAccount— the agent's tool calls, guarding against prompt injection in tool contentBoth correct. That was the point: the same rule implemented twice, in two packages, because the argument guaranteed nothing on its own. A third caller — a service calling another service — would have needed a third copy, and the failure mode of forgetting is silent cross-account access to
mail,index,images,events.There was no live hole: every other
service.Callsite is first-party Mu code building typed requests.Done (
1f6289f5)service.WithAccount(ctx, id)/service.AccountFrom(ctx)— identity on go-micro call metadata. Verified to propagate over the in-process transport before anything was built on it. An empty id clears inherited identity, so a guest request cannot borrow the previous caller's account.CallDynamicis the single enforcement point. It discardsargs["account_id"]and re-stamps from context, so no dynamic caller can scope a call to someone else by naming them.account_idoverwritten, guest cannot claim an account, identity reaches the handler when the caller passes nothing.The agent still sets the request field as well as the context, deliberately — its tool calls are dispatched by go-micro straight to the service rather than through
CallDynamic, so the field is what today's handlers read.Remaining
mail,index,images,events) to readservice.AccountFrom(ctx)instead of the request field.injectAccountand dropAccountIDfrom those request structs, so the value has one source.internal/service/service.go:95,store.NewFileStoreinto$HOME/.mu/store) — no namespace, no owner, no encryption, unlikeinternal/userdb. This is what the personal services need (reminders, ledger, notes, habits, contacts) and it must be settled before the service contract inmicro/xyzAdd full markets page at /markets with extended market data and persistent caching #7, or shipped services need their data migrated.Blocks:
micro/xyz#7 · related #1444, #1445