Skip to content

[EPIC] Strip NyxID organization concept — owner-only goals/sessions #275

@chronoai-shining

Description

@chronoai-shining

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_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 bare Vec<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.rs create.org_login + POST /orgs/{org}/repos; repo_create_classify.rs SSO-org URL parsing.
  • routes/goals.rs CreateRepoSpecBody.org_login, CreateRepoSpec.org_login, is_org_repo, install_hint_message wording.
  • routes/repos.rs SetupRepoRef, 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.

Definition of Done (epic)

  • All sub-issues closed and merged into develop.
  • grep -rn org_id backend/ returns only GitHub-org_login / repo-placement hits and the org-admin role-string fixtures.
  • cargo test -p fkst-control-plane -p fkst-shared green; cargo build -p fkst-worker green (no utoipa, no org symbols); tests/openapi.rs green.
  • GET /api/v1/goals no longer 503s; goals/sessions are owner-only.
  • GitHub-org org_login repo placement is verifiably untouched.

Labels: epic

Metadata

Metadata

Labels

backendRust/Axum backend workepicA tracking epic spanning multiple sub-issues

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions