[arch] auth/user mgmt make sure is defined well & understood #191
Labels
No milestone
No project
No assignees
5 participants
Notifications
Due date
Dependencies
No dependencies set.
Reference
lhumina_code/home#191
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
deliverable
spec in this hero_skill repo under architecture, make sure all understand well how this works
and its in line with architecture
What's the question
hero_proxyis the edge — terminates TLS, runs auth (bearer / OAuth / secp256k1 signature / IP-auto-login), and injects three identity headers on every forwarded request:X-Hero-User: <username>— the authenticated principalX-Hero-Context: <integer>— context id, sourced fromproxy.users.contextsince commit5f7bb04(proxy #23, "source X-Hero-Context from authenticated user, not route")X-Hero-Claims: <comma-separated>— flattened RBAC claims via group → role → claim BFSFive backends I audited handle these five different ways. There's no stack convention, no portable client across services, and one big load-bearing question about hero_osis's identity domain that needs an answer before any convention can land.
State today (5 backends, 5 different answers)
X-Hero-User?X-Hero-Context?X-Hero-Claims?external_idcol?user.me--auth-mode=devexternal_id, decoupled from proxyImplications:
X-Hero-Userhas full identity in collab, no reachable identity in whiteboard (noexternal_idto map onto), is effectively anonymous in voice/slides, and is the wrong shape for agent (which expects a JWT).X-Hero-Claimsis wire-defined but operationally inert. Collab logs it; agent forwards it to MCP downstreams; nobody makes a permission decision on it. The proxy spends BFS work computing it on every request.X-Hero-Contextis sourced and injected but no service scopes data by it. The denormalizedproxy.users.contextsource decision (5f7bb04) settled the proxy side; the downstream side is unanswered.Concrete reference impl (one possible answer for the collab side)
Recently merged in hero_collab as one option for "how a downstream service consumes proxy-injected identity":
feat(auth): bootstrap collab admin from proxy.is_admin + dev-mode honesty— on firstuser.me, querieshero_proxy_sdk::users_list(3s timeout) and grants collabadminif proxy reportsis_admin=1. Falls back to "first-user-on-empty-DB grants admin" when proxy is unreachable (mirrors hero_osis'sfirst user_create gets Owner role). Plus a 4-line shim that drops upstreamX-Hero-Userwhen--auth-mode=dev(collab declares itself the source of truth for identity in dev mode).This is collab's local choice, not a stack convention. Nothing about #21 obliges voice / slides / whiteboard / agent to do anything similar.
The load-bearing question — what is hero_osis's identity domain for?
hero_osis'sidentityschema definesUser,Group,Session,Device,Profile,Contact,SshKey, plus a full ed25519 challenge-responseAuthService. None of it is in the request path of any web-facing app:userstable; after5f7bb04it explicitly does NOT live-sync identity from osis (thecontext_sync.rsmodule that briefly existed was reverted within hours of being added).X-Hero-Userfrom proxy; never queries osis for identity.hero_osis_sdkand connects at startup, but the connection is not in the auth path.Meanwhile osis's
communicationdomain is being actively developed (e.g.list_messagesaddede0e02ddon Apr 19). So the codebase is alive — identity just isn't in the operational flow.Three plausible explanations the team needs to pick one of:
proxy.users; for key-based clients, it'sosis.User."proxy.usersis transitional.5f7bb04's revert was tactical (that specific sync was wrong), not strategic (osis irrelevant). If so: path and timeline?Without a chosen answer, every new app maintainer faces the same fork ("wire to proxy or osis?") with no canonical answer; the default ends up being "wire to neither" (voice, slides) or "wire to proxy and ignore the rest" (collab).
Questions
hero_osis's identity domain for? (one of the three above, or a fourth I'm missing)X-Hero-Userhandling? Reference impl in collab PR #21 — do other services adopt this pattern, or is per-service freedom intentional?X-Hero-Claims: enforce it or drop it? Currently nobody decides on it; the proxy spends compute producing it on every request.Question 1 precedes the others — the answer changes whether
external_idshould map toproxy.users.idorosis.User.sid, and whetherdisplay_nameshould be sourced from proxy or osis. If there's appetite for a one-page "identity contract for hero apps" that codifies the answers, happy to draft it.I agree it is very important to settle this.
@despiegk @timur have been working on this refactoring I think they should share their POV.
Thanks @sameh-farouk
Thanks @sameh-farouk — this audit is great and it lines up almost exactly with what Kristof outlined in today's meeting. Sharing what we now know, and where we still need an answer.
What's decided
proxy.usersstays. Apps consumeX-Hero-User/X-Hero-Context/X-Hero-Claimsfrom the proxy.engineers.tickets.read,engineers.projects.one, etc.). Apps match by prefix (engineers.*). Apps shouldn't carry their own notion of groups/users except where they genuinely have to (e.g. collab, user-to-user).users.<username>is set by the proxy so a user can always edit their own profile without an explicit role.X-Hero-Claimsstays and gets enforced — that's the whole authorization model going forward, not dead weight.What's still open
Kristof asked @lee to take ownership of the proxy track: read the current
hero_proxy, identify what's missing, add the OAuth flows, and then help every service integrate. The stack-wide identity convention you're asking for is essentially the deliverable of that work.Two things specifically would benefit from Lee's eyes once he's read the code:
hero_osis's identity domain for? (web flows useproxy.users, but the role of the osis identity schema — native/key-based clients, future state, or orphaned — wasn't settled in the meeting.) @lee, after you've read through, please weigh in here so we can lock the answer.external_idmapping in PR #21 is the canonical pattern for every service, or if there's a cleaner shape now that proxy is the source of truth.Let's keep this issue open — it's the right place to converge once Lee has a concrete proposal.
Alright, so from what I understood from my meeting with Kristof, the proxy is the only authentication provider. The proxy inserts claims before forwarding the request. Backend services must not have a separate login/identity system.
With regards to this, I believe hero_osis should remove (deprecated) identity/authentication fields.
All access/authentication is based on
claimsinjected by the proxy. Since backend services might also care about users/groups, it would be best to create and document a list of reserved claims. It seems this is already done with theadminclaim. Others, to start, should beuser.XXXwhereXXXis then the username, andgroup.XXXwith XXX being the group name (can be set multiple times). Also, claims will be restricted to^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}(\.[a-zA-Z0-9][a-zA-Z0-9_-]{0,62})*$see #191
give clear instructions
Convention question: how should hero_* services consume proxy-injected identity headers? + what is hero_osis's identity domain for?to story: auth/user mgmt make sure is defined well & understoodFormat: dot-separated ASCII, ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}(.[a-zA-Z0-9][a-zA-Z0-9_-]{0,62})*$, ≤ 255 chars, ≤ 8 segments, no wildcards (those go on the rule side). Default shape .. — e.g.
dns.zone.write. Validated by roles.add_claim.
Reserved prefixes (only proxy may emit):
Admin transition: the proxy seeds a reserved admin group; membership is auto-synced with users.is_admin (set/unset on add/update; backfilled at startup; manual desync rejected). During the deprecation window both admin and
groups.admin are emitted for is_admin=true users. New rules should match groups.admin; the legacy admin claim will be removed once backends have migrated.
story: auth/user mgmt make sure is defined well & understoodto [arch] auth/user mgmt make sure is defined well & understoodKeeping this open. The model is now agreed at the architecture level: the proxy is the single sign in edge and injects the identity and claims headers, identity is the Forge account, and authorization is claims based, documented in the shared authorization skill. What remains is the convergence across backends that still handle these headers differently and the hero_osis identity question. A short restatement of what remains would help close it.
Signed-by: mik-tf mik-tf@noreply.invalid