Team Mapping and Sync
Shoehorn synchronizes team memberships from identity providers (IdPs), Kubernetes clusters, and GitHub. This keeps team ownership up to date without manual maintenance.
Sync Sources
Section titled “Sync Sources”Teams and ownership data flow into Shoehorn from multiple sources:
| Source | Mechanism | What Gets Synced |
|---|---|---|
| Identity Provider | JWT group claims on login | User-to-team membership |
| OrgData Sync | Periodic API pull | Users and groups from Okta, Zitadel |
| Kubernetes | Agent namespace labels | Workload-to-team ownership |
| GitHub | Crawler topic discovery | Repository-to-team ownership |
Identity Provider Group Sync
Section titled “Identity Provider Group Sync”How It Works
Section titled “How It Works”- Configure your IdP to include a
groupsclaim in the JWT token - Create group mappings linking IdP groups to Shoehorn teams
- When a user logs in, Shoehorn reads their groups and updates team memberships
Supported Providers
Section titled “Supported Providers”| Provider | Group Claim | Setup |
|---|---|---|
| Zitadel | groups (included by default) | Zitadel setup |
| Okta | groups (requires claim configuration) | Okta setup |
| Keycloak | groups (realm groups) | Identity Providers |
Creating Group Mappings
Section titled “Creating Group Mappings”Map an IdP group to a Shoehorn team via the API:
curl -X POST https://shoehorn.example.com/api/v1/admin/teams/<team-id>/group-mappings \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "group_id": "engineering-platform", "idp_provider": "zitadel" }'Or via the UI:
- Navigate to Organization > Teams
- Select a team
- Go to the Group Mappings tab
- Click Add Mapping
- Enter the IdP group name and provider
- Click Save
Mapping Rules
Section titled “Mapping Rules”- Each IdP group maps to exactly one Shoehorn team
- A team can have multiple group mappings (from one or more providers)
- Mappings are one-way: IdP group -> Shoehorn team
- Changes to IdP group membership are reflected on next user login
- All changes are recorded in the team audit log
OrgData Sync (Proactive)
Section titled “OrgData Sync (Proactive)”OrgData sync periodically pulls users and groups from identity providers without waiting for individual logins.
Configuration
Section titled “Configuration”auth: orgdata: enabled: true providers: ["okta", "zitadel"] # Providers to sync from primaryProvider: "zitadel" # Wins in conflicts oktaApiTokenSecret: name: integration-credentials key: okta_api_tokenMulti-Provider Deduplication
Section titled “Multi-Provider Deduplication”When syncing from multiple providers:
- Users: Deduplicated by email address
- Teams: Deduplicated by name
- Conflicts: The primary provider wins
Required Credentials
Section titled “Required Credentials”| Provider | Secret Key | How to Generate |
|---|---|---|
| Zitadel | service-user-pat | Zitadel console > Service Users > PAT |
| Okta | okta_api_token | Okta admin > Security > API > Tokens |
Kubernetes Team Inference
Section titled “Kubernetes Team Inference”The K8s agent automatically infers team ownership for discovered workloads.
Priority Order
Section titled “Priority Order”- Workload annotation
shoehorn.dev/team(highest priority) - Workload annotation
shoehorn.dev/owner - Workload label
owner - Namespace label
shoehorn.dev/team - Namespace name pattern (e.g.,
payments-prod->payments) - Default:
unassigned
Namespace Labels
Section titled “Namespace Labels”Label namespaces to assign all workloads in that namespace to a team:
kubectl label namespace payments shoehorn.dev/team=payments-teamAll workloads in the payments namespace are then owned by payments-team.
Workload Annotations
Section titled “Workload Annotations”Override the namespace-level team for individual workloads:
apiVersion: apps/v1kind: Deploymentmetadata: name: payment-processor annotations: shoehorn.dev/team: payments-teamGitHub Topic-Based Ownership
Section titled “GitHub Topic-Based Ownership”The crawler assigns ownership to repositories based on GitHub topics.
Convention
Section titled “Convention”Add a topic following the pattern team-<team-slug>:
| GitHub Topic | Maps to Team |
|---|---|
team-platform-engineering | platform-engineering |
team-payments | payments |
team-data-engineering | data-engineering |
Priority
Section titled “Priority”GitHub topics have the lowest priority for ownership:
- Manifest
ownerfield (highest) - Kubernetes annotation
shoehorn.dev/owner - GitHub topic
team-*(lowest)
See GitHub Topics for details.
Ownership Priority Summary
Section titled “Ownership Priority Summary”When multiple sources provide ownership for the same entity:
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | Manifest owner field | owner: [{type: team, id: payments}] |
| 2 | Kubernetes annotation | shoehorn.dev/team: payments |
| 3 | Namespace label | shoehorn.dev/team: payments on namespace |
| 4 | GitHub topic | team-payments topic on repository |
| 5 (lowest) | Default | unassigned |
Terraform-Managed Teams
Section titled “Terraform-Managed Teams”Teams can also be managed as infrastructure-as-code using the Terraform provider:
resource "shoehorn_team" "platform" { name = "Platform Engineering" slug = "platform-engineering" description = "Manages shared infrastructure and platform services" metadata = jsonencode({ cost_center = "CC-1234" slack_channel = "#platform-eng" oncall_rotation = "platform-primary" })}See Also
Section titled “See Also”- Teams - Team creation and member management
- Group Mappings - Map IdP groups to Shoehorn teams
- GitHub Topics - Topic-based ownership
- Entity Enrichment - K8s annotation-driven enrichment
- Annotations Reference - All supported K8s annotations