Skip to content

feat(abac): user-scoped private Tools/Agents/Skills via allowedPrincipals (#149) - #193

Open
k5s-bot[bot] wants to merge 2 commits into
mainfrom
feat/user-scoped-abac-149
Open

feat(abac): user-scoped private Tools/Agents/Skills via allowedPrincipals (#149)#193
k5s-bot[bot] wants to merge 2 commits into
mainfrom
feat/user-scoped-abac-149

Conversation

@k5s-bot

@k5s-bot k5s-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

Resolves #149 ("User Scoped Skills, Agents and Tools").

What & why

The issue asks for a way to mark a Skill, Agent, or Tool as privately scoped
to specific users
, in addition to the existing RBAC controls. RBAC today
(allowedRoles, ADR 0004) gates by role — a class of caller — which can't
express "only this person (or this handful) may see and use this resource"
without minting a throwaway one-person role and attaching it everywhere.

This adds an optional allowedPrincipals to the Tool, LocalTool,
Agent, and Skill CRDs — an ABAC (attribute-based access control) layer the
orchestrator enforces on top of RBAC. The design is written up in
ADR 0037.

Semantics

  • Empty / unset = public — RBAC alone gates the resource. Every existing CR
    keeps working unchanged.
  • Non-empty = private — a caller is a retrieval/selection candidate only
    when both hold: RBAC passes (allowedRoles ∩ callerRoles) and the
    caller's resolved principal (ADR 0030 §6 — identity.principal, falling back
    to subject; e.g. github:<login>) is one of allowedPrincipals.
  • The two axes are ANDed — private-scoping only narrows, never grants.
  • Skills carry no roles of their own (ADR 0011); they may carry their own
    allowedPrincipals, and their effective private set is the intersection of
    that with the allowedPrincipals of every referenced tool/agent that is
    itself private — so a skill can never widen a private tool's audience.
  • Fail-closed throughout, matching ADR 0004's discipline.

Changes

core-controller (Go):

  • AllowedPrincipals []string (optional) on ToolSpec, LocalToolSpec,
    AgentSpec, SkillSpec, with doc comments.
  • Regenerated zz_generated.deepcopy.go and the CRD manifests under
    config/crd/bases via make generate manifests (chart crds/ copies are
    gitignored build artifacts, synced by the same target).

agent-orchestrator (TS):

  • allowedPrincipals on ToolDescriptor/AgentDescriptor/SkillDescriptor,
    read from the CRDs by every registry.
  • callerPrincipal added to the tool/agent/skill query filters and enforced in
    all three Qdrant stores (query + getByIds) via a shared
    vector-store/qdrant-abac.ts helper (payload gains allowedPrincipals + a
    denormalized private flag; the filter ANDs a public OR names-me clause).
  • derive-access.ts extended to derive the skill's effective principal set.
  • graph.ts threads the principal through one callerFilter(identity) used at
    every store query/getByIds site — so ABAC is enforced identically on RAG
    retrieval, the active-skill/active-agent re-checks, IntegrationRoute forced
    targets, and a skill's tool resolution. A private resource never enters the
    candidate/skill-tool set, so it can never be planned or launched.

Docs/tests: ADR 0037 (+ index), orchestrator README, and unit tests across
the stores, registries, and skill-access derivation (incl. inheritance,
intersection, disjoint/unreachable, and private-respond-only cases).

Verification

  • apps/agent-orchestrator: tsc --noEmit clean; full vitest suite 592
    passed
    .
  • controllers/core-controller: go build ./... and go vet ./api/... clean;
    make generate manifests regenerates cleanly.

Notes / out of scope

  • allowedPrincipals gates discovery & selection; it's orthogonal to
    identityProviders (which governs whose credential a launch runs with).
  • Not addressed here (deliberately): wildcard/group principals, auto-adding the
    creating user as an owner, and any management UI — listing principals on the
    CR is the v1 scope; groups can layer on later without changing the filter.

Maintainer: apply the ai-review label to this PR to request an automated
code review, or the ai-triage label to have review feedback addressed and
the branch brought back in sync with its base. (The automation can't label its
own PR, so this needs a human.)

…pals

Resolves #149. Adds an optional `allowedPrincipals` to the Tool, LocalTool,
Agent, and Skill CRDs — an ABAC private-scoping layer enforced by the
orchestrator ON TOP of the existing RBAC `allowedRoles` filter (ADR 0036).

Empty/unset = public (unchanged). Non-empty = private: a caller is a retrieval
candidate only if RBAC passes AND their resolved principal (ADR 0030 §6,
`identity.principal ?? subject`) is listed. Skills derive their effective
private set by intersecting their own list with any private tool/agent they
reference, so a skill can never widen a private tool's audience.

- core-controller: `AllowedPrincipals` on Tool/LocalTool/Agent/Skill specs;
  regenerated deepcopy + CRD manifests.
- agent-orchestrator: `allowedPrincipals` on the descriptors, read from the
  CRDs by every registry; `callerPrincipal` added to the tool/agent/skill
  query filters; enforced in all three Qdrant stores (query + getByIds) via a
  shared qdrant-abac helper; skill audience derivation extended; graph threads
  the principal through a single `callerFilter(identity)` at every store call.
- Fail-closed and rolling-upgrade safe (explicit `private` flag on upsert).
- ADR 0036 + README/docs; unit tests across stores, registries, derivation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PGNNomuXvbzwgcaF6gxDb
@k5s-bot

k5s-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
Author

🤖 Starting work on this now. Watch live or take over the session here: https://claude.ai/code/session_01WvxjeetdHb5EnvWqTNjb48

@k5s-bot

k5s-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
Author

ai-triage: brought back in sync with main

Feedback reviewed. There were no review threads or line comments on this PR (the only prior comment was the automation's start-of-run note), so there was nothing to reply to or resolve. The one actionable item was that the branch was CONFLICTING with main.

What I changed

  • Merged origin/main into the branch (following this repo's history convention — feature branches merge main in, they aren't rebased). The single content conflict was in docs/adr/README.md.
  • Resolved an ADR-number collision. While this PR was open, main merged ADR 0036: run the agent loop as Temporal workflows, behind AGENT_ENGINE #194, which claimed ADR 0036 for the Temporal execution engine. This PR had also used 0036. I renumbered this PR's ADR to 0037:
    • docs/adr/0036-user-scoped-abac-private-resources.mddocs/adr/0037-user-scoped-abac-private-resources.md (git rename; heading updated to # 0037.).
    • Kept main's 0036 row in the ADR index and added a 0037 row for this ADR.
    • Updated every in-repo reference from ADR 0036 / docs/adr/0036 to 0037 across the Go API types, the regenerated CRD manifests, the orchestrator TS source, tests, and README. The engines/temporal/** tree's own 0036 reference was left untouched (it correctly points at ADR 0036: run the agent loop as Temporal workflows, behind AGENT_ENGINE #194's ADR).
    • Updated this PR description's ADR link/number to 0037.
  • main's new work is confined to engines/temporal/** and a few top-level files; it made zero changes under apps/agent-orchestrator/ or controllers/core-controller/, so there was no semantic conflict with this PR's code — only the ADR number needed reconciling.

Verification (local, post-merge)

  • controllers/core-controller: go build ./... and go vet ./api/... clean; make generate manifests regenerates with no diff (manifests/deepcopy in sync).
  • apps/agent-orchestrator: tsc --noEmit clean; full vitest suite 592 passed (46 files).

Deliberately not done

  • No code/behavior changes beyond the merge and the ADR renumber — the ABAC implementation itself was unchanged, since no review raised anything about it.

CI is re-running on the merge commit (the merge also introduced main's new Go temporal-engine job, which is independent of this PR's changes); it was green on the pre-merge commit and I've locally re-verified the core-controller and orchestrator checks above.

@k5s-bot

k5s-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown
Author

Done. Here's a summary of what I did on PR #193.

What the feedback was

  • No review threads or line comments existed on the PR (gh api .../reviews and .../comments both returned 0; the only issue comment was the automation's start-of-run note). So there was nothing to reply to or resolve.
  • All checks were green, but the branch was CONFLICTING with main — that was the real actionable item.

What I changed

  1. Merged origin/main into the branch (this repo merges main into feature branches rather than rebasing — I matched that convention). The single content conflict was in docs/adr/README.md.
  2. Resolved an ADR-number collision. While this PR was open, main merged ADR 0036: run the agent loop as Temporal workflows, behind AGENT_ENGINE #194, which took ADR 0036 for the Temporal execution engine — the same number this PR used. I renumbered this PR's ADR to 0037:
    • git mv of the ADR file → 0037-user-scoped-abac-private-resources.md, heading updated to # 0037.
    • Kept main's 0036 index row, added a 0037 row.
    • Updated every in-repo reference (ADR 0036 / docs/adr/0036) → 0037 across Go API types, regenerated CRD manifests, orchestrator TS source, tests, and README — leaving the engines/temporal/** tree's own 0036 reference alone (it correctly points at ADR 0036: run the agent loop as Temporal workflows, behind AGENT_ENGINE #194's ADR).
    • Updated the PR description's ADR link/number to 0037.
  3. Confirmed main's new work is confined to engines/temporal/** + top-level files — zero changes under `apps/agent-orchest…

✅ Opened pull request: imaustink/agent-controller#193

@k5s-bot k5s-bot Bot removed the ai-triage label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User Scoped Skills, Agents and Tools

2 participants