[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7QB6DF06DXV6251NJ124M":3,"skill-same:01M6F7QB6DF06DXV6251NJ124M":74},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":58,"updatedAt":59,"install":60,"resources":73},"01M6F7QB6DF06DXV6251NJ124M","langfuse-enterprise-rbac",null,"Configure Langfuse enterprise organization management and access control.\nUse when implementing team access controls, configuring organization settings,\nor setting up role-based permissions for Langfuse projects.\nTrigger with phrases like \"langfuse RBAC\", \"langfuse teams\",\n\"langfuse organization\", \"langfuse access control\", \"langfuse permissions\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1981,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Flangfuse-pack\u002Fskills\u002Flangfuse-enterprise-rbac\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"Enterprise","langfuse","langfuse enterprise rbac","rbac","edit","when","for","implementing","controls","trigger","io","Access Control","organization","up","use","write","read","projects","configure","access","team","permissions","teams","2026-11-10 10:12:30",false,"---\nname: langfuse-enterprise-rbac\ndescription: |\n  Configure Langfuse enterprise organization management and access control.\n  Use when implementing team access controls, configuring organization settings,\n  or setting up role-based permissions for Langfuse projects.\n  Trigger with phrases like \"langfuse RBAC\", \"langfuse teams\",\n  \"langfuse organization\", \"langfuse access control\", \"langfuse permissions\".\nallowed-tools: Read, Write, Edit\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\n---\n\n# Langfuse Enterprise RBAC\n\n## Overview\nConfigure enterprise-grade access control for Langfuse projects and organizations.\n\n## Prerequisites\n- Langfuse organization\u002Fteam account\n- Understanding of role-based access patterns\n- SSO provider (optional, for enterprise SSO)\n\n## Role Definitions\n\n| Role | Dashboard | API Read | API Write | Settings | Billing |\n|------|-----------|----------|-----------|----------|---------|\n| Owner | Full | Full | Full | Full | Full |\n| Admin | Full | Full | Full | Full | View |\n| Member | Full | Full | Full | None | None |\n| Viewer | View | Read | None | None | None |\n| API Only | None | Full | Full | None | None |\n\n## Instructions\n\n### Step 1: Implement Role-Based Access in Your Application\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Frbac.ts\n\nenum LangfuseRole {\n  Owner = \"owner\",\n  Admin = \"admin\",\n  Member = \"member\",\n  Viewer = \"viewer\",\n  ApiOnly = \"api_only\",\n}\n\ninterface LangfusePermissions {\n  canViewDashboard: boolean;\n  canReadTraces: boolean;\n  canWriteTraces: boolean;\n  canManageSettings: boolean;\n  canManageMembers: boolean;\n  canManageBilling: boolean;\n  canCreateApiKeys: boolean;\n  canDeleteData: boolean;\n}\n\nconst ROLE_PERMISSIONS: Record\u003CLangfuseRole, LangfusePermissions> = {\n  [LangfuseRole.Owner]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: true,\n    canManageMembers: true,\n    canManageBilling: true,\n    canCreateApiKeys: true,\n    canDeleteData: true,\n  },\n  [LangfuseRole.Admin]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: true,\n    canManageMembers: true,\n    canManageBilling: false,\n    canCreateApiKeys: true,\n    canDeleteData: true,\n  },\n  [LangfuseRole.Member]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n  [LangfuseRole.Viewer]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: false,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n  [LangfuseRole.ApiOnly]: {\n    canViewDashboard: false,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n};\n\nfunction hasPermission(\n  role: LangfuseRole,\n  permission: keyof LangfusePermissions\n): boolean {\n  return ROLE_PERMISSIONS[role][permission];\n}\n\nfunction checkPermission(\n  role: LangfuseRole,\n  permission: keyof LangfusePermissions\n): void {\n  if (!hasPermission(role, permission)) {\n    throw new ForbiddenError(\n      `Permission denied: ${permission} requires higher role than ${role}`\n    );\n  }\n}\n```\n\n### Step 2: Implement API Key Scoping\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fscoped-keys.ts\n\ninterface ScopedApiKey {\n  id: string;\n  publicKey: string;\n  secretKey: string;\n  name: string;\n  scope: ApiKeyScope;\n  createdAt: Date;\n  createdBy: string;\n  expiresAt?: Date;\n}\n\ninterface ApiKeyScope {\n  permissions: (\"read\" | \"write\")[];\n  projects?: string[];       \u002F\u002F Restrict to specific projects\n  environments?: string[];   \u002F\u002F Restrict to specific environments\n  ipAllowlist?: string[];    \u002F\u002F Restrict by IP\n  rateLimit?: number;        \u002F\u002F Custom rate limit\n}\n\nclass ScopedLangfuseClient {\n  private langfuse: Langfuse;\n  private scope: ApiKeyScope;\n\n  constructor(apiKey: ScopedApiKey) {\n    this.langfuse = new Langfuse({\n      publicKey: apiKey.publicKey,\n      secretKey: apiKey.secretKey,\n    });\n    this.scope = apiKey.scope;\n  }\n\n  trace(params: Parameters\u003Ctypeof this.langfuse.trace>[0]) {\n    \u002F\u002F Check write permission\n    if (!this.scope.permissions.includes(\"write\")) {\n      throw new ForbiddenError(\"API key does not have write permission\");\n    }\n\n    \u002F\u002F Check project restriction\n    if (this.scope.projects?.length) {\n      const projectId = params.metadata?.projectId;\n      if (projectId && !this.scope.projects.includes(projectId)) {\n        throw new ForbiddenError(`API key not authorized for project ${projectId}`);\n      }\n    }\n\n    return this.langfuse.trace(params);\n  }\n\n  async fetchTraces(params: any) {\n    \u002F\u002F Check read permission\n    if (!this.scope.permissions.includes(\"read\")) {\n      throw new ForbiddenError(\"API key does not have read permission\");\n    }\n\n    return this.langfuse.fetchTraces(params);\n  }\n}\n```\n\n### Step 3: Implement Project-Based Access Control\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fprojects.ts\n\ninterface LangfuseProject {\n  id: string;\n  name: string;\n  organizationId: string;\n  environment: \"development\" | \"staging\" | \"production\";\n  members: ProjectMember[];\n}\n\ninterface ProjectMember {\n  userId: string;\n  email: string;\n  role: LangfuseRole;\n  addedAt: Date;\n  addedBy: string;\n}\n\nclass ProjectAccessController {\n  private projects: Map\u003Cstring, LangfuseProject> = new Map();\n\n  async getUserProjects(userId: string): Promise\u003CLangfuseProject[]> {\n    return Array.from(this.projects.values()).filter((project) =>\n      project.members.some((m) => m.userId === userId)\n    );\n  }\n\n  async getUserRole(userId: string, projectId: string): Promise\u003CLangfuseRole | null> {\n    const project = this.projects.get(projectId);\n    if (!project) return null;\n\n    const member = project.members.find((m) => m.userId === userId);\n    return member?.role || null;\n  }\n\n  async addMember(\n    projectId: string,\n    member: Omit\u003CProjectMember, \"addedAt\">,\n    addedBy: string\n  ): Promise\u003Cvoid> {\n    \u002F\u002F Check if adder has permission\n    const adderRole = await this.getUserRole(addedBy, projectId);\n    if (!adderRole || !hasPermission(adderRole, \"canManageMembers\")) {\n      throw new ForbiddenError(\"Cannot add members to this project\");\n    }\n\n    \u002F\u002F Prevent privilege escalation\n    if (this.isHigherRole(member.role, adderRole)) {\n      throw new ForbiddenError(\"Cannot add member with higher role than yourself\");\n    }\n\n    const project = this.projects.get(projectId);\n    if (!project) throw new NotFoundError(\"Project not found\");\n\n    project.members.push({\n      ...member,\n      addedAt: new Date(),\n      addedBy,\n    });\n  }\n\n  private isHigherRole(role1: LangfuseRole, role2: LangfuseRole): boolean {\n    const hierarchy = [\n      LangfuseRole.Viewer,\n      LangfuseRole.ApiOnly,\n      LangfuseRole.Member,\n      LangfuseRole.Admin,\n      LangfuseRole.Owner,\n    ];\n    return hierarchy.indexOf(role1) > hierarchy.indexOf(role2);\n  }\n}\n```\n\n### Step 4: Implement SSO Integration\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fsso.ts\n\ninterface SSOConfig {\n  provider: \"okta\" | \"azure\" | \"google\" | \"saml\";\n  domain: string;\n  clientId: string;\n  clientSecret: string;\n  callbackUrl: string;\n  groupMapping: Record\u003Cstring, LangfuseRole>;\n}\n\nclass LangfuseSSO {\n  private config: SSOConfig;\n\n  constructor(config: SSOConfig) {\n    this.config = config;\n  }\n\n  \u002F\u002F Map SSO groups to Langfuse roles\n  mapGroupsToRole(groups: string[]): LangfuseRole {\n    for (const group of groups) {\n      if (this.config.groupMapping[group]) {\n        return this.config.groupMapping[group];\n      }\n    }\n    return LangfuseRole.Viewer; \u002F\u002F Default role\n  }\n\n  \u002F\u002F SAML assertion handler\n  async handleSAMLAssertion(assertion: any): Promise\u003C{\n    userId: string;\n    email: string;\n    role: LangfuseRole;\n    groups: string[];\n  }> {\n    const email = assertion.user.email;\n    const groups = assertion.user.groups || [];\n\n    \u002F\u002F Verify domain\n    if (!email.endsWith(`@${this.config.domain}`)) {\n      throw new UnauthorizedError(\"Email domain not allowed\");\n    }\n\n    return {\n      userId: assertion.user.id,\n      email,\n      role: this.mapGroupsToRole(groups),\n      groups,\n    };\n  }\n}\n\n\u002F\u002F Usage\nconst ssoConfig: SSOConfig = {\n  provider: \"okta\",\n  domain: \"company.com\",\n  clientId: process.env.OKTA_CLIENT_ID!,\n  clientSecret: process.env.OKTA_CLIENT_SECRET!,\n  callbackUrl: \"https:\u002F\u002Fapp.company.com\u002Fauth\u002Fcallback\",\n  groupMapping: {\n    \"Engineering-Admins\": LangfuseRole.Admin,\n    \"Engineering\": LangfuseRole.Member,\n    \"Data-Team\": LangfuseRole.Viewer,\n  },\n};\n```\n\n### Step 5: Implement Audit Logging for RBAC\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Frbac-audit.ts\n\ninterface RBACEvent {\n  timestamp: Date;\n  action:\n    | \"member_added\"\n    | \"member_removed\"\n    | \"role_changed\"\n    | \"api_key_created\"\n    | \"api_key_revoked\"\n    | \"permission_denied\";\n  actor: string;\n  target: string;\n  projectId?: string;\n  details: Record\u003Cstring, any>;\n}\n\nclass RBACAuditLogger {\n  async log(event: Omit\u003CRBACEvent, \"timestamp\">) {\n    const auditEvent: RBACEvent = {\n      ...event,\n      timestamp: new Date(),\n    };\n\n    \u002F\u002F Log to audit trail\n    console.log(\"[RBAC Audit]\", JSON.stringify(auditEvent));\n\n    \u002F\u002F Store in database\n    await this.persist(auditEvent);\n\n    \u002F\u002F Alert on sensitive actions\n    if (event.action === \"permission_denied\") {\n      await this.alertSecurityTeam(auditEvent);\n    }\n  }\n\n  private async persist(event: RBACEvent) {\n    \u002F\u002F Store in audit database\n  }\n\n  private async alertSecurityTeam(event: RBACEvent) {\n    \u002F\u002F Send alert for potential security issues\n  }\n\n  async query(options: {\n    actor?: string;\n    action?: string;\n    projectId?: string;\n    from?: Date;\n    to?: Date;\n  }): Promise\u003CRBACEvent[]> {\n    \u002F\u002F Query audit log\n    return [];\n  }\n}\n\nexport const rbacAudit = new RBACAuditLogger();\n```\n\n## Output\n- Role-based permission system\n- Scoped API key implementation\n- Project-based access control\n- SSO integration\n- RBAC audit logging\n\n## Access Control Matrix\n\n| Action | Owner | Admin | Member | Viewer | API Only |\n|--------|-------|-------|--------|--------|----------|\n| View traces | Yes | Yes | Yes | Yes | No |\n| Create traces | Yes | Yes | Yes | No | Yes |\n| Delete traces | Yes | Yes | No | No | No |\n| Manage members | Yes | Yes | No | No | No |\n| Create API keys | Yes | Yes | No | No | No |\n| Manage billing | Yes | No | No | No | No |\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Permission denied | Insufficient role | Request role upgrade |\n| SSO login fails | Wrong group mapping | Update group mapping |\n| API key rejected | Expired or scoped | Create new key |\n| Project not found | No access | Request project access |\n\n## Resources\n- [Langfuse Organizations](https:\u002F\u002Flangfuse.com\u002Fdocs\u002Fteams)\n- [Langfuse API Keys](https:\u002F\u002Flangfuse.com\u002Fdocs\u002Fapi-reference)\n- [SAML 2.0 Specification](https:\u002F\u002Fwiki.oasis-open.org\u002Fsecurity\u002FFrontPage)\n\n## Next Steps\nFor complex migrations, see `langfuse-migration-deep-dive`.\n","# Langfuse Enterprise RBAC\n\n## Overview\nConfigure enterprise-grade access control for Langfuse projects and organizations.\n\n## Prerequisites\n- Langfuse organization\u002Fteam account\n- Understanding of role-based access patterns\n- SSO provider (optional, for enterprise SSO)\n\n## Role Definitions\n\n| Role | Dashboard | API Read | API Write | Settings | Billing |\n|------|-----------|----------|-----------|----------|---------|\n| Owner | Full | Full | Full | Full | Full |\n| Admin | Full | Full | Full | Full | View |\n| Member | Full | Full | Full | None | None |\n| Viewer | View | Read | None | None | None |\n| API Only | None | Full | Full | None | None |\n\n## Instructions\n\n### Step 1: Implement Role-Based Access in Your Application\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Frbac.ts\n\nenum LangfuseRole {\n  Owner = \"owner\",\n  Admin = \"admin\",\n  Member = \"member\",\n  Viewer = \"viewer\",\n  ApiOnly = \"api_only\",\n}\n\ninterface LangfusePermissions {\n  canViewDashboard: boolean;\n  canReadTraces: boolean;\n  canWriteTraces: boolean;\n  canManageSettings: boolean;\n  canManageMembers: boolean;\n  canManageBilling: boolean;\n  canCreateApiKeys: boolean;\n  canDeleteData: boolean;\n}\n\nconst ROLE_PERMISSIONS: Record\u003CLangfuseRole, LangfusePermissions> = {\n  [LangfuseRole.Owner]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: true,\n    canManageMembers: true,\n    canManageBilling: true,\n    canCreateApiKeys: true,\n    canDeleteData: true,\n  },\n  [LangfuseRole.Admin]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: true,\n    canManageMembers: true,\n    canManageBilling: false,\n    canCreateApiKeys: true,\n    canDeleteData: true,\n  },\n  [LangfuseRole.Member]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n  [LangfuseRole.Viewer]: {\n    canViewDashboard: true,\n    canReadTraces: true,\n    canWriteTraces: false,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n  [LangfuseRole.ApiOnly]: {\n    canViewDashboard: false,\n    canReadTraces: true,\n    canWriteTraces: true,\n    canManageSettings: false,\n    canManageMembers: false,\n    canManageBilling: false,\n    canCreateApiKeys: false,\n    canDeleteData: false,\n  },\n};\n\nfunction hasPermission(\n  role: LangfuseRole,\n  permission: keyof LangfusePermissions\n): boolean {\n  return ROLE_PERMISSIONS[role][permission];\n}\n\nfunction checkPermission(\n  role: LangfuseRole,\n  permission: keyof LangfusePermissions\n): void {\n  if (!hasPermission(role, permission)) {\n    throw new ForbiddenError(\n      `Permission denied: ${permission} requires higher role than ${role}`\n    );\n  }\n}\n```\n\n### Step 2: Implement API Key Scoping\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fscoped-keys.ts\n\ninterface ScopedApiKey {\n  id: string;\n  publicKey: string;\n  secretKey: string;\n  name: string;\n  scope: ApiKeyScope;\n  createdAt: Date;\n  createdBy: string;\n  expiresAt?: Date;\n}\n\ninterface ApiKeyScope {\n  permissions: (\"read\" | \"write\")[];\n  projects?: string[];       \u002F\u002F Restrict to specific projects\n  environments?: string[];   \u002F\u002F Restrict to specific environments\n  ipAllowlist?: string[];    \u002F\u002F Restrict by IP\n  rateLimit?: number;        \u002F\u002F Custom rate limit\n}\n\nclass ScopedLangfuseClient {\n  private langfuse: Langfuse;\n  private scope: ApiKeyScope;\n\n  constructor(apiKey: ScopedApiKey) {\n    this.langfuse = new Langfuse({\n      publicKey: apiKey.publicKey,\n      secretKey: apiKey.secretKey,\n    });\n    this.scope = apiKey.scope;\n  }\n\n  trace(params: Parameters\u003Ctypeof this.langfuse.trace>[0]) {\n    \u002F\u002F Check write permission\n    if (!this.scope.permissions.includes(\"write\")) {\n      throw new ForbiddenError(\"API key does not have write permission\");\n    }\n\n    \u002F\u002F Check project restriction\n    if (this.scope.projects?.length) {\n      const projectId = params.metadata?.projectId;\n      if (projectId && !this.scope.projects.includes(projectId)) {\n        throw new ForbiddenError(`API key not authorized for project ${projectId}`);\n      }\n    }\n\n    return this.langfuse.trace(params);\n  }\n\n  async fetchTraces(params: any) {\n    \u002F\u002F Check read permission\n    if (!this.scope.permissions.includes(\"read\")) {\n      throw new ForbiddenError(\"API key does not have read permission\");\n    }\n\n    return this.langfuse.fetchTraces(params);\n  }\n}\n```\n\n### Step 3: Implement Project-Based Access Control\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fprojects.ts\n\ninterface LangfuseProject {\n  id: string;\n  name: string;\n  organizationId: string;\n  environment: \"development\" | \"staging\" | \"production\";\n  members: ProjectMember[];\n}\n\ninterface ProjectMember {\n  userId: string;\n  email: string;\n  role: LangfuseRole;\n  addedAt: Date;\n  addedBy: string;\n}\n\nclass ProjectAccessController {\n  private projects: Map\u003Cstring, LangfuseProject> = new Map();\n\n  async getUserProjects(userId: string): Promise\u003CLangfuseProject[]> {\n    return Array.from(this.projects.values()).filter((project) =>\n      project.members.some((m) => m.userId === userId)\n    );\n  }\n\n  async getUserRole(userId: string, projectId: string): Promise\u003CLangfuseRole | null> {\n    const project = this.projects.get(projectId);\n    if (!project) return null;\n\n    const member = project.members.find((m) => m.userId === userId);\n    return member?.role || null;\n  }\n\n  async addMember(\n    projectId: string,\n    member: Omit\u003CProjectMember, \"addedAt\">,\n    addedBy: string\n  ): Promise\u003Cvoid> {\n    \u002F\u002F Check if adder has permission\n    const adderRole = await this.getUserRole(addedBy, projectId);\n    if (!adderRole || !hasPermission(adderRole, \"canManageMembers\")) {\n      throw new ForbiddenError(\"Cannot add members to this project\");\n    }\n\n    \u002F\u002F Prevent privilege escalation\n    if (this.isHigherRole(member.role, adderRole)) {\n      throw new ForbiddenError(\"Cannot add member with higher role than yourself\");\n    }\n\n    const project = this.projects.get(projectId);\n    if (!project) throw new NotFoundError(\"Project not found\");\n\n    project.members.push({\n      ...member,\n      addedAt: new Date(),\n      addedBy,\n    });\n  }\n\n  private isHigherRole(role1: LangfuseRole, role2: LangfuseRole): boolean {\n    const hierarchy = [\n      LangfuseRole.Viewer,\n      LangfuseRole.ApiOnly,\n      LangfuseRole.Member,\n      LangfuseRole.Admin,\n      LangfuseRole.Owner,\n    ];\n    return hierarchy.indexOf(role1) > hierarchy.indexOf(role2);\n  }\n}\n```\n\n### Step 4: Implement SSO Integration\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Fsso.ts\n\ninterface SSOConfig {\n  provider: \"okta\" | \"azure\" | \"google\" | \"saml\";\n  domain: string;\n  clientId: string;\n  clientSecret: string;\n  callbackUrl: string;\n  groupMapping: Record\u003Cstring, LangfuseRole>;\n}\n\nclass LangfuseSSO {\n  private config: SSOConfig;\n\n  constructor(config: SSOConfig) {\n    this.config = config;\n  }\n\n  \u002F\u002F Map SSO groups to Langfuse roles\n  mapGroupsToRole(groups: string[]): LangfuseRole {\n    for (const group of groups) {\n      if (this.config.groupMapping[group]) {\n        return this.config.groupMapping[group];\n      }\n    }\n    return LangfuseRole.Viewer; \u002F\u002F Default role\n  }\n\n  \u002F\u002F SAML assertion handler\n  async handleSAMLAssertion(assertion: any): Promise\u003C{\n    userId: string;\n    email: string;\n    role: LangfuseRole;\n    groups: string[];\n  }> {\n    const email = assertion.user.email;\n    const groups = assertion.user.groups || [];\n\n    \u002F\u002F Verify domain\n    if (!email.endsWith(`@${this.config.domain}`)) {\n      throw new UnauthorizedError(\"Email domain not allowed\");\n    }\n\n    return {\n      userId: assertion.user.id,\n      email,\n      role: this.mapGroupsToRole(groups),\n      groups,\n    };\n  }\n}\n\n\u002F\u002F Usage\nconst ssoConfig: SSOConfig = {\n  provider: \"okta\",\n  domain: \"company.com\",\n  clientId: process.env.OKTA_CLIENT_ID!,\n  clientSecret: process.env.OKTA_CLIENT_SECRET!,\n  callbackUrl: \"https:\u002F\u002Fapp.company.com\u002Fauth\u002Fcallback\",\n  groupMapping: {\n    \"Engineering-Admins\": LangfuseRole.Admin,\n    \"Engineering\": LangfuseRole.Member,\n    \"Data-Team\": LangfuseRole.Viewer,\n  },\n};\n```\n\n### Step 5: Implement Audit Logging for RBAC\n\n```typescript\n\u002F\u002F lib\u002Flangfuse\u002Frbac-audit.ts\n\ninterface RBACEvent {\n  timestamp: Date;\n  action:\n    | \"member_added\"\n    | \"member_removed\"\n    | \"role_changed\"\n    | \"api_key_created\"\n    | \"api_key_revoked\"\n    | \"permission_denied\";\n  actor: string;\n  target: string;\n  projectId?: string;\n  details: Record\u003Cstring, any>;\n}\n\nclass RBACAuditLogger {\n  async log(event: Omit\u003CRBACEvent, \"timestamp\">) {\n    const auditEvent: RBACEvent = {\n      ...event,\n      timestamp: new Date(),\n    };\n\n    \u002F\u002F Log to audit trail\n    console.log(\"[RBAC Audit]\", JSON.stringify(auditEvent));\n\n    \u002F\u002F Store in database\n    await this.persist(auditEvent);\n\n    \u002F\u002F Alert on sensitive actions\n    if (event.action === \"permission_denied\") {\n      await this.alertSecurityTeam(auditEvent);\n    }\n  }\n\n  private async persist(event: RBACEvent) {\n    \u002F\u002F Store in audit database\n  }\n\n  private async alertSecurityTeam(event: RBACEvent) {\n    \u002F\u002F Send alert for potential security issues\n  }\n\n  async query(options: {\n    actor?: string;\n    action?: string;\n    projectId?: string;\n    from?: Date;\n    to?: Date;\n  }): Promise\u003CRBACEvent[]> {\n    \u002F\u002F Query audit log\n    return [];\n  }\n}\n\nexport const rbacAudit = new RBACAuditLogger();\n```\n\n## Output\n- Role-based permission system\n- Scoped API key implementation\n- Project-based access control\n- SSO integration\n- RBAC audit logging\n\n## Access Control Matrix\n\n| Action | Owner | Admin | Member | Viewer | API Only |\n|--------|-------|-------|--------|--------|----------|\n| View traces | Yes | Yes | Yes | Yes | No |\n| Create traces | Yes | Yes | Yes | No | Yes |\n| Delete traces | Yes | Yes | No | No | No |\n| Manage members | Yes | Yes | No | No | No |\n| Create API keys | Yes | Yes | No | No | No |\n| Manage billing | Yes | No | No | No | No |\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Permission denied | Insufficient role | Request role upgrade |\n| SSO login fails | Wrong group mapping | Update group mapping |\n| API key rejected | Expired or scoped | Create new key |\n| Project not found | No access | Request project access |\n\n## Resources\n- [Langfuse Organizations](https:\u002F\u002Flangfuse.com\u002Fdocs\u002Fteams)\n- [Langfuse API Keys](https:\u002F\u002Flangfuse.com\u002Fdocs\u002Fapi-reference)\n- [SAML 2.0 Specification](https:\u002F\u002Fwiki.oasis-open.org\u002Fsecurity\u002FFrontPage)\n\n## Next Steps\nFor complex migrations, see `langfuse-migration-deep-dive`.\n",{"name":5,"author":47,"license":15,"version":48,"description":49,"allowed-tools":50},"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Configure Langfuse enterprise organization management and access control.\nUse when implementing team access controls, configuring organization settings,\nor setting up role-based permissions for Langfuse projects.\nTrigger with phrases like \"langfuse RBAC\", \"langfuse teams\",\n\"langfuse organization\", \"langfuse access control\", \"langfuse permissions\".\n","Read, Write, Edit","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Flangfuse-pack\u002Fskills\u002Flangfuse-enterprise-rbac",231,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-12 03:15:57",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill langfuse-enterprise-rbac",{"tool":66,"command":67},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill langfuse-enterprise-rbac",{"tool":69,"command":70},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill langfuse-enterprise-rbac","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Flangfuse-pack\u002Fskills\u002Flangfuse-enterprise-rbac\u002FSKILL.md",true,[],[75,114,139,165,192,214],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":80,"sourceHtmlUrl":81,"categories":82,"keywords":88,"publishedAt":42},"01M6FEGPPK0ZTKCCM0R3E3KS33","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[83],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,33,107,108,109,110,111,25,112,113],"web artifacts builder","web","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","html","single-file","simple","ui components","multi","jsx","suite",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":118,"sourceHtmlUrl":119,"categories":120,"keywords":125,"publishedAt":42},"01M6FEE9VTK1W4GY21AKREJXRG","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[121,124],{"categoryNo":122,"categoryName":123,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},[126,127,128,94,129,130,90,131,132,133,104,134,135,136,137,138,25],"testing","webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local","with",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":143,"sourceHtmlUrl":144,"categories":145,"keywords":146,"publishedAt":42},"01M6FE20F7ZTQBFWKF4MPJ2SBX","neuropixels-analysis","Neuropixels neural recording analysis. Load SpikeGLX\u002FOpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen\u002FIBL curation, AI-assisted visual analysis, for Neuropixels 1.0\u002F2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FSKILL.md",[],[147,148,149,150,151,152,153,154,33,155,156,157,158,159,160,161,138,162,163,164,25,24],"analysis","neuropixels analysis","spike","metrics","data","open","quality","user","recordings","Neural","motion","visual","quality metrics","unit","sorting","curation","recording","load",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":169,"sourceHtmlUrl":170,"categories":171,"keywords":175,"publishedAt":42},"01M6FDP0PDK6F1GGX395C7625Z","skill-creator","Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FSKILL.md",[172],{"categoryNo":173,"categoryName":174,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000023","Skill与智能体开发",[176,177,178,179,180,181,182,183,184,185,186,187,188,189,138,25,190,24,191],"skill creator","creator","workflows","create","update","capabilities","knowledge","tool","integrations","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":193,"name":194,"nameZh":6,"description":195,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":196,"sourceHtmlUrl":197,"categories":198,"keywords":200,"publishedAt":42},"01M6FDM8Y7VK7NYG9SM7QPA3PW","notebooklm","Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FSKILL.md",[199],{"categoryNo":173,"categoryName":174,"taxonomyType":86,"taxonomyTypeDesc":87},[194,201,202,203,204,205,206,133,207,208,33,209,210,211,212,25,213],"google","browser automation","gemini","automation","auth","code","library","management","only","document","query","Citation","source",{"skillNo":215,"name":216,"nameZh":6,"description":217,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":79,"licenseFileUrl":218,"sourceHtmlUrl":219,"categories":220,"keywords":227,"publishedAt":42},"01M6FDHPXE10J3XGTCH5HBHS1M","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FSKILL.md",[221,224],{"categoryNo":222,"categoryName":223,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":225,"categoryName":226,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000016","图像与多媒体",[228,229,230,177,231,105,232,233,182,33,234,189,235,236,237,25,238,24],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]