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
Remove the NyxID-organization concept (NyxID's user-grouping for shared resources) entirely from fkst-hosted, leaving a pure OWNER-ONLY model. Goals and sessions become owned by owner_user_id only. Authorization collapses to: admin-permission bypass → owner == caller → legacy-ownerless allow → deny. No org ownership, no org sharing, no org-membership authz, and no NyxID GET /api/v1/orgs call anywhere.
This tracks the milestone Strip NyxID organization concept (owner-only model) and follows the just-shipped v0.2.2 owner-only credential redesign (the NyxID service account + RFC8693 token-exchange were already removed; authz already dropped the SA-based org_role/org_exists/user_exists). The remaining user-token-based org logic (user_orgs / visible_org_ids / require_org_writer / org_id DTO+model plumbing) must now ALSO go.
Owner-only end state
GoalDoc / SessionDoc carry only owner_user_id (+ existing non-org fields). No org_id.
The object-layer predicate is allows(ctx, Ownership { owner_user_id }, action) — a pure, IO-free owner+admin+legacy check with no NyxID coupling and no Unavailable failure mode.
POST /api/v1/goals, GET /api/v1/goals, POST /api/v1/goals/:id/trigger, POST /api/v1/goals/submit, POST /api/v1/repos/:owner/:name/fkst-setup make no org membership/visibility calls; a non-owner non-admin gets a plain 403/404.
The goal-issue hidden marker (v:1) no longer writes org_id; already-filed goal issues that carry org_id remain forward-readable (parse-and-ignore, no version bump, no migration).
NyxIdClient no longer has user_orgs/orgs_cache/cache_ttl; the nyxid_org_cache_ttl_secs config knob is gone.
Live bug fixed by this work (note in #271 and #270)
Today GET /api/v1/goals hard-503s authorization service unavailable: visible_org_ids calls NyxIdClient::user_orgs, which deserializes a bareVec<OrgSummary> while prod NyxID actually returns {"orgs":[...]}; the serde error maps to AppError::Unavailable. The in-file test masks it by mocking the wrong bare-array shape. Removing the whole path eliminates the 503 — do NOT merely patch the deserialization.
OUT OF SCOPE — KEEP INTACT (do NOT touch)
These match an org grep but are the GitHub-organization repo-placement feature, unrelated to NyxID orgs:
goals/repo_create.rscreate.org_login + POST /orgs/{org}/repos; repo_create_classify.rs SSO-org URL parsing.
routes/repos.rsSetupRepoRef, SetupResponse.repo, the owner/name path params, validate_repo_ref.
The literal role string"org-admin" test fixtures in auth/identity.rs and auth/middleware.rs (a NyxID role-claim, not an org).
Sub-issues (depends/blocks ordering)
Ordered so the workspace compiles and all tests pass at every commit (CLAUDE.md). The cross-crate field deletions (GoalDoc.org_id, SessionDoc.org_id, Ownership.org_id) and the NyxIdClient::new signature change have consumers in several crates, so the consumer-side edits must land with or before the definition removals.
Summary
Remove the NyxID-organization concept (NyxID's user-grouping for shared resources) entirely from fkst-hosted, leaving a pure OWNER-ONLY model. Goals and sessions become owned by
owner_user_idonly. Authorization collapses to: admin-permission bypass → owner == caller → legacy-ownerless allow → deny. No org ownership, no org sharing, no org-membership authz, and no NyxIDGET /api/v1/orgscall anywhere.This tracks the milestone Strip NyxID organization concept (owner-only model) and follows the just-shipped v0.2.2 owner-only credential redesign (the NyxID service account + RFC8693 token-exchange were already removed; authz already dropped the SA-based
org_role/org_exists/user_exists). The remaining user-token-based org logic (user_orgs/visible_org_ids/require_org_writer/org_idDTO+model plumbing) must now ALSO go.Owner-only end state
GoalDoc/SessionDoccarry onlyowner_user_id(+ existing non-org fields). Noorg_id.allows(ctx, Ownership { owner_user_id }, action)— a pure, IO-free owner+admin+legacy check with no NyxID coupling and noUnavailablefailure mode.POST /api/v1/goals,GET /api/v1/goals,POST /api/v1/goals/:id/trigger,POST /api/v1/goals/submit,POST /api/v1/repos/:owner/:name/fkst-setupmake no org membership/visibility calls; a non-owner non-admin gets a plain 403/404.v:1) no longer writesorg_id; already-filed goal issues that carryorg_idremain forward-readable (parse-and-ignore, no version bump, no migration).NyxIdClientno longer hasuser_orgs/orgs_cache/cache_ttl; thenyxid_org_cache_ttl_secsconfig knob is gone.Live bug fixed by this work (note in #271 and #270)
Today
GET /api/v1/goalshard-503sauthorization service unavailable:visible_org_idscallsNyxIdClient::user_orgs, which deserializes a bareVec<OrgSummary>while prod NyxID actually returns{"orgs":[...]}; the serde error maps toAppError::Unavailable. The in-file test masks it by mocking the wrong bare-array shape. Removing the whole path eliminates the 503 — do NOT merely patch the deserialization.OUT OF SCOPE — KEEP INTACT (do NOT touch)
These match an
orggrep but are the GitHub-organization repo-placement feature, unrelated to NyxID orgs:goals/repo_create.rscreate.org_login+POST /orgs/{org}/repos;repo_create_classify.rsSSO-org URL parsing.routes/goals.rsCreateRepoSpecBody.org_login,CreateRepoSpec.org_login,is_org_repo,install_hint_messagewording.routes/repos.rsSetupRepoRef,SetupResponse.repo, theowner/namepath params,validate_repo_ref."org-admin"test fixtures inauth/identity.rsandauth/middleware.rs(a NyxID role-claim, not an org).Sub-issues (depends/blocks ordering)
Ordered so the workspace compiles and all tests pass at every commit (CLAUDE.md). The cross-crate field deletions (
GoalDoc.org_id,SessionDoc.org_id,Ownership.org_id) and theNyxIdClient::newsignature change have consumers in several crates, so the consumer-side edits must land with or before the definition removals.type:refactor,sa:downstream-authz): dropOwnership.org_id, theorg_roleparam + Rule-3 fromallows(), deletevisible_org_ids()andrequire_org_writer(), simplify all four call sites (goals::create/list/trigger,goals_submit::authorize_object,repos::fkst_setup), drop theSetupRequest.org_idfield, adjustGoalIssueStore::listto dropvisible_org_ids. This alone kills the 503. Blocks Remove user_orgs/OrgSummary/OrgRole/orgs_cache from fkst-shared + nyxid_org_cache_ttl_secs #271.type:chore): deleteuser_orgs/OrgSummary/OrgRole/CachedOrgs/orgs_cache/ORGS_PATH, dropcache_ttlfromNyxIdClient::new, cascade tostartup.rs/main.rs/config.rs(nyxid_org_cache_ttl_secs). Depends on Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270 (which removes the lastOrgRole/user_orgs/cache_ttlconsumers).type:refactor): deleteGoalDoc.org_id,SessionDoc.org_id(cross-crate),CreateGoalRequest.org_id,GoalView.org_id,SessionView.org_id, theGoalMarker.org_id(parse-and-ignore back-compat, NO version bump), and every internal carrier (GoalTriggerInfo,SessionOwner,build_goal, the twoGoalView/SessionViewmappings). Depends on Strip NyxID-org authz: remove visible_org_ids/require_org_writer/OrgRole branches (owner-only) #270 (it removesOwnership.org_idso the ownership-builder edits compile).type:refactor): drop the already-no-oporg_idparams fromvault::list_for_scope,codex_provider::{is_chrono_llm_connected,resolve_provider_choice,resolve_from_entries}, and thesession.org_id.as_deref()call sites inservice.rs. Depends on Remove org_id from goal/session models, DTOs (OpenAPI), and the goal-issue marker #272 (it removesSessionDoc.org_id, the source of these args). May be merged into Remove org_id from goal/session models, DTOs (OpenAPI), and the goal-issue marker #272 if scoping prefers.type:chore): repo-widegrep -rn org_idmust return onlyorg_login/GitHub-org hits; add the GET /api/v1/goals no-503 regression, the owner-only trigger/submit-Forbidden tests, the parse-and-ignore marker test, and optionally a negative OpenAPI guard that no schema exposesorg_id. Depends on Remove org_id from goal/session models, DTOs (OpenAPI), and the goal-issue marker #272 and Drop the no-op org_id pass-through params from vault/codex_provider/session dispatch #273.Definition of Done (epic)
develop.grep -rn org_id backend/returns only GitHub-org_login/ repo-placement hits and theorg-adminrole-string fixtures.cargo test -p fkst-control-plane -p fkst-sharedgreen;cargo build -p fkst-workergreen (no utoipa, no org symbols);tests/openapi.rsgreen.GET /api/v1/goalsno longer 503s; goals/sessions are owner-only.org_loginrepo placement is verifiably untouched.Labels: epic