Backstage Governance and Permissions¶
This guide covers governance models and permission policies for SkillMeat artifacts exposed through your Backstage catalog integration. It explains how to control who can view, deploy, and modify SkillMeat artifacts within Backstage's permission framework.
Table of Contents¶
- Prerequisites
- Backstage Permission Model Overview
- SkillMeat RBAC Integration
- Role and Group Mapping
- Policy Configuration
- Policy Examples
- Audit Logging
- Common Governance Patterns
- Troubleshooting
Prerequisites¶
Before configuring governance policies, ensure you have:
- Backstage instance running with SkillMeat plugin installed (see Plugin Installation)
- SkillMeat catalog integration configured (see Catalog Integration)
- SkillMeat enterprise edition deployed with role-based access control enabled
- Backstage Permissions Framework enabled in your Backstage app-config
- System admin access to both Backstage and SkillMeat (for policy configuration)
Verifying Plugin Installation¶
Confirm the SkillMeat plugin is loaded in your Backstage instance:
# Check plugin is registered in app-config.yaml
grep -A 5 'skillmeat:' app-config.yaml
# Should output:
# skillmeat:
# baseUrl: https://sam.internal
Verifying Permissions Framework¶
Verify Backstage Permissions Framework is enabled:
# Check app-config.yaml for permission policy configuration
grep -A 10 'permission:' app-config.yaml
Backstage supports multiple permission backends; SkillMeat integrates with the standard Backstage permission API. See Backstage Permissions Documentation for full framework details.
Backstage Permission Model Overview¶
Backstage's permission system uses a policy-as-code model where administrators define rules that determine what actions users can perform. SkillMeat integrates with Backstage's permission API to enforce artifact-level access control.
Key Concepts¶
Resources: SkillMeat artifacts exposed in the Backstage catalog (skills, commands, agents, etc.) are Backstage resources that can be gated by permissions.
Actions: Backstage defines standard actions that can be performed on resources:
- catalog:entity:read — View an artifact in the catalog
- catalog:entity:write — Modify an artifact (edit metadata, content)
- catalog:entity:delete — Delete an artifact from the catalog
Policies: Rules that map identities (users/groups) and resource attributes to allowed actions.
Identity: Backstage resolves user identity from your identity provider (IDP). Users belong to groups (teams) that influence permission decisions.
How SkillMeat Integrates¶
When a user attempts to:
- View a SkillMeat artifact in Backstage → Backstage permission system checks
catalog:entity:readpermission - Edit artifact details (tags, description, etc.) → Backstage checks
catalog:entity:write - Deploy the artifact → SkillMeat backend enforces additional scope-based checks (see SkillMeat RBAC Integration)
The permission check happens at the Backstage layer; the artifact deployment enforces SkillMeat-side RBAC.
SkillMeat RBAC Integration¶
SkillMeat's role-based access control (RBAC) system operates in parallel with Backstage permissions. Understanding both layers is essential for comprehensive governance.
SkillMeat Roles¶
SkillMeat defines four hierarchical roles that control organizational access:
| Role | Scope | Permissions |
|---|---|---|
| system_admin | Enterprise-wide | Full control: create/update/delete any artifact, deploy globally, manage users and teams, configure governance |
| team_admin | Team-scoped | Manage artifacts within team, manage team membership and roles, view enterprise artifacts (read-only) |
| team_member | Team-scoped | Create and modify artifacts in team scope, cannot modify enterprise artifacts |
| viewer | Read-only | View shared artifacts, no creation or modification capabilities |
SkillMeat Scopes¶
SkillMeat uses ownership scopes to determine access:
| Scope | Owner Type | Visible To | Writable By |
|---|---|---|---|
| user | Individual user | Owner + system_admin | Owner + system_admin |
| team | Team collection | Team members + system_admin | Team members with owner/admin role + system_admin |
| enterprise | Organization-wide | All users (with appropriate roles) | system_admin only |
Integration Pattern¶
Backstage permissions determine visibility in the catalog view; SkillMeat RBAC determines what actions are allowed on the artifact:
Backstage Permission Check
↓
User can view artifact in catalog? (catalog:entity:read)
↓
SkillMeat RBAC Check
↓
Can user perform the requested action?
↓
Action allowed/denied
Example: A team_member might have Backstage catalog:entity:read permission on all artifacts, but SkillMeat RBAC blocks them from modifying an enterprise artifact (which only system_admin can edit).
Role and Group Mapping¶
Map your Backstage identity provider groups to SkillMeat roles to establish role hierarchy and permissions.
Identity Provider Configuration¶
Your Backstage instance connects to an IDP (e.g., Okta, Google Workspace, Microsoft Entra) that provides:
- User identity (email, ID)
- Group membership (team names, roles)
SkillMeat reads these from Backstage's AuthContext and uses them to resolve role and scope.
Mapping Table¶
Configure your Backstage IDP connector to map groups to SkillMeat roles:
| Okta Group | Google Workspace Group | Microsoft Entra Group | SkillMeat Role | Scope |
|---|---|---|---|---|
engineering-team |
engineering@company.com |
Engineering Team |
team_admin |
Team |
platform-team |
platform@company.com |
Platform Team |
system_admin |
Enterprise |
data-team |
data@company.com |
Data Science Team |
team_admin |
Team |
all-users |
all-users@company.com |
All Users |
viewer |
Read-only |
Configuring Backstage IDP Connector¶
In your Backstage app-config.yaml, configure the auth provider to map groups to roles:
# Okta example
auth:
providers:
okta:
development:
clientId: ${OKTA_CLIENT_ID}
clientSecret: ${OKTA_CLIENT_SECRET}
audience: ${OKTA_AUDIENCE}
# Group mapping resolved by Backstage auth resolver
authorizationUrl: ${OKTA_AUTHORIZATION_URL}
# Google example
auth:
providers:
google:
development:
clientId: ${GOOGLE_CLIENT_ID}
clientSecret: ${GOOGLE_CLIENT_SECRET}
# Groups resolved from Google Workspace directory
See Backstage Auth Providers Documentation for provider-specific setup.
Role Resolution Flow¶
When a user logs into Backstage:
- IDP verifies credentials and returns user identity + groups
- Backstage auth resolver maps groups to SkillMeat roles
AuthContextis populated withuser_id,roles, andscopes- SkillMeat API requests include this context for permission checks
Policy Configuration¶
Define policies that control what actions authenticated users can perform. Policies are expressed in Backstage's permission policy language.
Policy Location¶
Backstage permission policies are configured in:
- Development:
packages/backend/src/plugins/permission.ts(or equivalent in your structure) - Production:
app-config.yamlor external policy service
Creating a Basic Policy¶
The SkillMeat Backstage plugin integrates with Backstage's standard catalog permissions. Define policies for these actions:
Example policy (TypeScript):
// packages/backend/src/plugins/permission.ts
import { BackstageIdentityResponse } from '@backstage/plugin-auth-node';
import {
AuthorizeResult,
PolicyDecision,
isResourcePermission,
isPermission,
} from '@backstage/plugin-permission-common';
import {
catalogEntityCreatePermission,
catalogEntityDeletePermission,
catalogEntityReadPermission,
catalogEntityWritePermission,
} from '@backstage/plugin-catalog-common';
export const permissionPolicy = async (
request: PermissionEvaluationResult,
user: BackstageIdentityResponse,
): Promise<PolicyDecision> => {
// Allow all authenticated users to read catalog entities
if (
isPermission(request, catalogEntityReadPermission) &&
user.identity
) {
return { result: AuthorizeResult.ALLOW };
}
// Only team admins and system admins can write
if (
isPermission(request, catalogEntityWritePermission) &&
user.identity
) {
const roles = user.identity.getOptionalClaims().roles || [];
if (
roles.includes('team_admin') ||
roles.includes('system_admin')
) {
return { result: AuthorizeResult.ALLOW };
}
return { result: AuthorizeResult.DENY };
}
// Only system admins can delete
if (
isPermission(request, catalogEntityDeletePermission) &&
user.identity
) {
const roles = user.identity.getOptionalClaims().roles || [];
if (roles.includes('system_admin')) {
return { result: AuthorizeResult.ALLOW };
}
return { result: AuthorizeResult.DENY };
}
// Default deny
return { result: AuthorizeResult.DENY };
};
Resource-Based Policies¶
For more granular control, policies can check resource attributes (artifact type, owner, etc.):
// Restrict deployment of enterprise artifacts
if (
isResourcePermission(request, skillmeatArtifactDeployPermission) &&
user.identity
) {
const artifact = request.resourceRef;
const roles = user.identity.getOptionalClaims().roles || [];
// Only system admins can deploy enterprise artifacts
if (artifact.owner_type === 'enterprise') {
return {
result: roles.includes('system_admin')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
// Team members can deploy within their team
const userTeams = user.identity.getOptionalClaims().teams || [];
if (
artifact.owner_type === 'team' &&
userTeams.includes(artifact.owner_id)
) {
return { result: AuthorizeResult.ALLOW };
}
return { result: AuthorizeResult.DENY };
}
Policy Examples¶
Real-world governance patterns for common scenarios.
Example 1: Allow Team Members to Browse, Restrict Deployment¶
Goal: Team members can view all SkillMeat artifacts in the catalog but cannot deploy enterprise artifacts.
Policy:
// Grant read access to all authenticated users
if (isPermission(request, catalogEntityReadPermission)) {
return { result: user.identity ? AuthorizeResult.ALLOW : AuthorizeResult.DENY };
}
// Grant write access only to team_admin and system_admin
if (isPermission(request, catalogEntityWritePermission)) {
const roles = user.identity?.getOptionalClaims().roles || [];
const canWrite =
roles.includes('team_admin') ||
roles.includes('system_admin');
return {
result: canWrite ? AuthorizeResult.ALLOW : AuthorizeResult.DENY,
};
}
// Deployment check (in SkillMeat backend, not Backstage)
// SkillMeat API enforces: only system_admin can deploy enterprise artifacts
Example 2: Restrict Destructive Actions to Admins¶
Goal: Only system administrators can delete artifacts; prevent accidental deletions.
Policy:
// Only system_admin can delete
if (isPermission(request, catalogEntityDeletePermission)) {
const roles = user.identity?.getOptionalClaims().roles || [];
return {
result: roles.includes('system_admin')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
Example 3: Enforce Artifact-Type-Specific Rules¶
Goal: Only the platform team can deploy MCP servers; other teams can deploy skills and commands.
Policy:
// Check artifact type before allowing deployment
if (isResourcePermission(request, skillmeatArtifactDeployPermission)) {
const artifact = request.resourceRef;
const roles = user.identity?.getOptionalClaims().roles || [];
const teams = user.identity?.getOptionalClaims().teams || [];
// Only platform team can deploy MCP servers
if (artifact.artifact_type === 'mcp_server') {
return {
result: teams.includes('platform-team')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
// Other teams can deploy skills/commands
if (['skill', 'command', 'agent'].includes(artifact.artifact_type)) {
const isTeamMember = teams.some(
team => team === artifact.owner_id
);
return {
result: isTeamMember || roles.includes('system_admin')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
return { result: AuthorizeResult.DENY };
}
Audit Logging¶
Track who performs what actions on SkillMeat artifacts for compliance and governance.
Where Actions Are Logged¶
| Action | SkillMeat Log | Backstage Log | Notes |
|---|---|---|---|
| View artifact in catalog | Not logged (read-only) | Backstage audit log (if enabled) | Low volume; configure in Backstage if needed |
| Create artifact | ✓ Artifact created event | ✓ Entity created event | Logged in both systems |
| Update artifact content | ✓ Artifact modified event | ✓ Entity updated event | Complete history in SkillMeat audit trail |
| Deploy artifact | ✓ Deployment event with target, outcome, enforcement mode | ✗ Not automatically logged | SkillMeat is source of truth for deployments |
| Delete artifact | ✓ Artifact deleted event | ✓ Entity deleted event | Permanent record in SkillMeat audit trail |
| Change permissions | ✓ Permission change event (ownership, enforcement toggle) | ✗ Backstage policy changes not logged to SkillMeat | Check Backstage logs for policy modifications |
Accessing the SkillMeat Audit Trail¶
SkillMeat enterprise edition maintains a complete audit log. Access it via:
Web UI (System Admin only): 1. Log in as system admin 2. Navigate to Admin → Enterprise Dashboard 3. Select Audit Trail tab 4. Filter by date, event type, artifact, or actor
API (System Admin only):
curl -X GET "http://localhost:8080/api/v1/enterprise/audit-trail?limit=50&offset=0" \
-H "Authorization: Bearer $SKILLMEAT_ADMIN_PAT"
Response:
{
"items": [
{
"id": "audit-001",
"timestamp": "2026-04-20T10:30:00Z",
"actor_id": "user-123",
"actor_email": "jane@company.com",
"action": "artifact_deployed",
"artifact_id": "skill-456",
"artifact_name": "data-analysis-skill",
"target_scope": "team",
"target_id": "data-team",
"outcome": "success",
"details": {
"enforcement_mode": "silent_override",
"version_deployed": "v2.1.0"
}
}
],
"total": 150,
"offset": 0,
"limit": 50
}
Backstage Audit Logging¶
If Backstage audit logging is enabled, check logs for:
# View Backstage permission decisions
# Location depends on your Backstage setup; typically:
# - Docker: container logs
# - Kubernetes: kubectl logs <backstage-pod>
# - Local dev: console output from yarn start
# Search for SkillMeat-related decisions:
grep -i 'skillmeat\|catalog.*permission' backstage.log
Compliance and Retention¶
SkillMeat audit trail: - Retention: Configurable; default 90 days (enterprise edition) - Immutable: Audit logs cannot be modified or deleted - Queryable: Filter by date range, actor, artifact, action type
Backstage audit log: - Depends on your Backstage audit plugin configuration - See Backstage Audit Logging for retention and export options
Common Governance Patterns¶
Pattern 1: Separation of Duties¶
Objective: Prevent any single person from both creating and deploying sensitive artifacts (compliance requirement).
Implementation:
- Create role:
artifact_creator(can create but not deploy) - Create role:
artifact_deployer(can deploy but not create) - Assign different people to each role
Policy:
if (isResourcePermission(request, skillmeatArtifactDeployPermission)) {
const roles = user.identity?.getOptionalClaims().roles || [];
// Only artifact_deployer role can deploy
return {
result: roles.includes('artifact_deployer')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
if (isPermission(request, catalogEntityWritePermission)) {
const roles = user.identity?.getOptionalClaims().roles || [];
// Only artifact_creator role can write
return {
result: roles.includes('artifact_creator')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
Pattern 2: Approval Workflows¶
Objective: Require approval before deploying critical artifacts.
Implementation:
- Create a
pending_deploymentstatus for artifacts - System admin reviews and approves before moving to
deployedstatus - Approval recorded in audit trail
Workflow (manual process, not automated):
- Developer creates artifact (status:
draft) - Developer marks for review (status:
pending_review) - System admin reviews in Backstage UI
- If approved, system admin deploys (status:
deployed) - Audit trail records: creator, reviewer, approver, timestamps
Pattern 3: Environment-Based Access¶
Objective: Different permissions for development, staging, and production artifacts.
Implementation:
Tag artifacts with environment:
- env:dev — Development artifacts
- env:staging — Staging artifacts
- env:prod — Production artifacts
Policy:
if (isResourcePermission(request, skillmeatArtifactDeployPermission)) {
const artifact = request.resourceRef;
const roles = user.identity?.getOptionalClaims().roles || [];
const teams = user.identity?.getOptionalClaims().teams || [];
// Dev artifacts: team members can deploy
if (artifact.tags.includes('env:dev')) {
return {
result: teams.length > 0 ? AuthorizeResult.ALLOW : AuthorizeResult.DENY,
};
}
// Staging artifacts: team admins only
if (artifact.tags.includes('env:staging')) {
return {
result: roles.includes('team_admin')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
// Production artifacts: system admins only
if (artifact.tags.includes('env:prod')) {
return {
result: roles.includes('system_admin')
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
};
}
return { result: AuthorizeResult.DENY };
}
Troubleshooting¶
Policy Not Being Applied¶
Symptom: Users can perform actions that should be denied by policy.
Diagnosis:
-
Verify Backstage Permissions Framework is enabled:
-
Check Backstage logs for permission evaluation errors:
-
Verify user has correct roles:
- Log in as user
- Check browser DevTools → Network →
/api/auth/identifyresponse - Verify
rolesarray includes expected roles
Solution: - Restart Backstage after policy changes - Verify IDP is configured correctly (see Role and Group Mapping) - Check that permission policy is registered in backend plugin
User Denied Unexpectedly¶
Symptom: User denied access to an artifact they should be able to access.
Diagnosis:
-
Check user's role:
-
Check team membership:
-
Check artifact ownership and visibility:
Solution: - Promote user to higher role if needed - Add user to team if accessing team-owned artifacts - Verify artifact is active (not archived or deleted) - Check Backstage permission policy includes the user's role
Audit Log Missing Entries¶
Symptom: Expected action not appearing in audit trail.
Diagnosis:
-
Verify audit logging is enabled:
-
Check action occurred in expected timeframe:
-
Verify user has audit read permissions:
- User must be
system_adminto query audit logs - Regular users cannot access audit trails
Solution: - Enable audit logging in SkillMeat config if disabled - Check that action completed successfully (failures may not be logged) - Verify you're querying the correct date range - Contact system admin if audit logging is disabled
Next Steps¶
- Enterprise Admin Workflow Guide — Manage artifacts, collections, and activity feeds
- Enterprise Governance Guide — Understand ownership models and enforcement
- Backstage Permissions Framework — Official Backstage permission documentation
- Backstage Auth Providers — Configure identity and group mapping